/* * INET An implementation of the TCP/IP protocol suite for the LINUX * operating system. INET is implemented using the BSD Socket * interface as the means of communication with the user level. * * Interface (streams) handling functions. * * Version: @(#)dev.c 1.0.19 05/31/93 * * Authors: Ross Biro, * Fred N. van Kempen, * Mark Evans, * * Fixes: * Alan Cox: check_addr returns a value for a wrong subnet * ie not us but don't forward this! * Alan Cox: block timer if the inet_bh handler is running * Alan Cox: generic queue code added. A lot neater now * C.E.Hawkins: SIOCGIFCONF only reports 'upped' interfaces * C.E.Hawkins: IFF_PROMISC support * Alan Cox: Supports Donald Beckers new hardware * multicast layer, but not yet multicast lists. * Alan Cox: ip_addr_match problems with class A/B nets. * C.E.Hawkins IP 0.0.0.0 and also same net route fix. [FIXME: Ought to cause ICMP_REDIRECT] * Alan Cox: Removed bogus subnet check now the subnet code * a) actually works for all A/B nets * b) doesn't forward off the same interface. * Alan Cox: Multiple extra protocols * Alan Cox: Fixed ifconfig up of dud device setting the up flag * Alan Cox: Fixed verify_area errors * Alan Cox: Removed IP_SET_DEV as per Fred's comment. I hope this doesn't give * anything away 8) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inet.h" #include "dev.h" #include "eth.h" #include "ip.h" #include "route.h" #include "protocol.h" #include "tcp.h" #include "skbuff.h" #include "sock.h" #include "arp.h" #ifdef CONFIG_AX25 #include "ax25.h" #endif #ifdef CONFIG_IPX static struct packet_type ipx_8023_type = { NET16(ETH_P_802_3), 0, ipx_rcv, NULL, NULL }; static struct packet_type ipx_packet_type = { NET16(ETH_P_IPX), 0, ipx_rcv, NULL, &ipx_8023_type }; #endif #ifdef CONFIG_AX25 static struct packet_type ax25_packet_type = { NET16(ETH_P_AX25), 0, ax25_rcv, NULL, #ifdef CONFIG_IPX &ipx_packet_type #else NULL #endif }; #endif static struct packet_type arp_packet_type = { NET16(ETH_P_ARP), 0, /* copy */ arp_rcv, NULL, #ifdef CONFIG_IPX #ifndef CONFIG_AX25 &ipx_packet_type #else &ax25_packet_type #endif #else #ifdef CONFIG_AX25 &ax25_packet_type #else NULL /* next */ #endif #endif }; static struct packet_type ip_packet_type = { NET16(ETH_P_IP), 0, /* copy */ ip_rcv, NULL, &arp_packet_type }; struct packet_type *ptype_base = &ip_packet_type; static struct sk_buff *volatile backlog = NULL; static unsigned long ip_bcast = 0; /* Return the lesser of the two values. */ static unsigned long min(unsigned long a, unsigned long b) { if (a < b) return(a); return(b); } /* Determine a default network mask, based on the IP address. */ static unsigned long get_mask(unsigned long addr) { unsigned long dst; if (addr == 0L) return(0L); /* special case */ dst = ntohl(addr); if (IN_CLASSA(dst)) return(htonl(IN_CLASSA_NET)); if (IN_CLASSB(dst)) return(htonl(IN_CLASSB_NET)); if (IN_CLASSC(dst)) return(htonl(IN_CLASSC_NET)); /* Something else, probably a subnet. */ return(0); } int ip_addr_match(unsigned long me, unsigned long him) { int i; unsigned long mask=0xFFFFFFFF; DPRINTF((DBG_DEV, "ip_addr_match(%s, ", in_ntoa(me))); DPRINTF((DBG_DEV, "%s)\n", in_ntoa(him))); if (me == him) return(1); for (i = 0; i < 4; i++, me >>= 8, him >>= 8, mask >>= 8) { if ((me & 0xFF) != (him & 0xFF)) { /* * The only way this could be a match is for * the rest of addr1 to be 0 or 255. */ if (me != 0 && me != mask) return(0); return(1); } } return(1); } /* Check the address for our address, broadcasts, etc. */ int chk_addr(unsigned long addr) { struct device *dev; unsigned long dst; DPRINTF((DBG_DEV, "chk_addr(%s) --> ", in_ntoa(addr))); dst = ntohl(addr); /* Accept both `all ones' and `all zeros' as BROADCAST. */ if (dst == INADDR_ANY || dst == INADDR_BROADCAST) { DPRINTF((DBG_DEV, "BROADCAST\n")); return(IS_BROADCAST); } /* Accept all of the `loopback' class A net. */ if ((dst & IN_CLASSA_NET) == 0x7F000000L) { DPRINTF((DBG_DEV, "LOOPBACK\n")); /* * We force `loopback' to be equal to MY_ADDR. */ return(IS_MYADDR); /* return(IS_LOOPBACK); */ } /* OK, now check the interface addresses. */ for (dev = dev_base; dev != NULL; dev = dev->next) { if (!(dev->flags&IFF_UP)) continue; if ((dev->pa_addr == 0)/* || (dev->flags&IFF_PROMISC)*/) return(IS_MYADDR); /* Is it the exact IP address? */ if (addr == dev->pa_addr) { DPRINTF((DBG_DEV, "MYADDR\n")); return(IS_MYADDR); } /* Nope. Check for a subnetwork broadcast. */ if ((addr & dev->pa_mask) == (dev->pa_addr & dev->pa_mask)) { if ((addr & ~dev->pa_mask) == 0) { DPRINTF((DBG_DEV, "SUBBROADCAST-0\n")); return(IS_BROADCAST); } if (((addr & ~dev->pa_mask) | dev->pa_mask) == INADDR_BROADCAST) { DPRINTF((DBG_DEV, "SUBBROADCAST-1\n")); return(IS_BROADCAST); } } /* Nope. Check for Network broadcast. */ if(IN_CLASSA(dst)) { if( addr == (dev->pa_addr | 0xffffff00)) { DPRINTF((DBG_DEV, "CLASS A BROADCAST-1\n")); return(IS_BROADCAST); } } else if(IN_CLASSB(dst)) { if( addr == (dev->pa_addr | 0xffff0000)) { DPRINTF((DBG_DEV, "CLASS B BROADCAST-1\n")); return(IS_BROADCAST); } } else { /* IN_CLASSC */ if( addr == (dev->pa_addr | 0xff000000)) { DPRINTF((DBG_DEV, "CLASS C BROADCAST-1\n")); return(IS_BROADCAST); } } } DPRINTF((DBG_DEV, "NONE\n")); return(0); /* no match at all */ } /* * Retrieve our own address. * Because the loopback address (127.0.0.1) is already recognized * automatically, we can use the loopback interface's address as * our "primary" interface. This is the addressed used by IP et * al when it doesn't know which address to use (i.e. it does not * yet know from or to which interface to go...). */ unsigned long my_addr(void) { struct device *dev; for (dev = dev_base; dev != NULL; dev = dev->next) { if (dev->flags & IFF_LOOPBACK) return(dev->pa_addr); } return(0); } static int dev_nit=0; /* Number of network taps running */ /* Add a protocol ID to the list. This will change soon. */ void dev_add_pack(struct packet_type *pt) { struct packet_type *p1; pt->next = ptype_base; /* Don't use copy counts on ETH_P_ALL. Instead keep a global count of number of these and use it and pt->copy to decide copies */ pt->copy=0; if(pt->type==NET16(ETH_P_ALL)) dev_nit++; /* I'd like a /dev/nit too one day 8) */ else { /* See if we need to copy it. */ for (p1 = ptype_base; p1 != NULL; p1 = p1->next) { if (p1->type == pt->type) { pt->copy = 1; break; } } } /* * NIT taps must go at the end or inet_bh will leak! */ if(pt->type==NET16(ETH_P_ALL)) { pt->next=NULL; if(ptype_base==NULL) ptype_base=pt; else { for(p1=ptype_base;p1->next!=NULL;p1=p1->next); p1->next=pt; } } else ptype_base = pt; } /* Remove a protocol ID from the list. This will change soon. */ void dev_remove_pack(struct packet_type *pt) { struct packet_type *lpt, *pt1; if (pt->type == NET16(ETH_P_ALL)) dev_nit--; if (pt == ptype_base) { ptype_base = pt->next; return; } lpt = NULL; for (pt1 = ptype_base; pt1->next != NULL; pt1 = pt1->next) { if (pt1->next == pt ) { cli(); if (!pt->copy && lpt) lpt->copy = 0; pt1->next = pt->next; sti(); return; } if (pt1->next -> type == pt ->type && pt->type != NET16(ETH_P_ALL)) { lpt = pt1->next; } } } /* Find an interface in the list. This will change soon. */ struct device * dev_get(char *name) { struct device *dev; for (dev = dev_base; dev != NULL; dev = dev->next) { if (strcmp(dev->name, name) == 0) return(dev); } return(NULL); } /* Find an interface that can handle addresses for a certain address. */ struct device * dev_check(unsigned long addr) { struct device *dev; for (dev = dev_base; dev; dev = dev->next) { if (!(dev->flags & IFF_UP)) continue; if (!(dev->flags & IFF_POINTOPOINT)) continue; if (addr != dev->pa_dstaddr) continue; return dev; } for (dev = dev_base; dev; dev = dev->next) { if (!(dev->flags & IFF_UP)) continue; if (dev->flags & IFF_POINTOPOINT) continue; if (dev->pa_mask & (addr ^ dev->pa_addr)) continue; return dev; } return NULL; } /* Prepare an interface for use. */ int dev_open(struct device *dev) { int ret = 0; if (dev->open) ret = dev->open(dev); if (ret == 0) dev->flags |= (IFF_UP | IFF_RUNNING); return(ret); } /* Completely shutdown an interface. */ int dev_close(struct device *dev) { if (dev->flags != 0) { int ct=0; dev->flags = 0; if (dev->stop) dev->stop(dev); rt_flush(dev); dev->pa_addr = 0; dev->pa_dstaddr = 0; dev->pa_brdaddr = 0; dev->pa_mask = 0; /* Purge any queued packets when we down the link */ while(ctbuffs[ct]))!=NULL) if(skb->free) kfree_skb(skb,FREE_WRITE); ct++; } } return(0); } /* Send (or queue for sending) a packet. */ void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri) { int where = 0; /* used to say if the packet should go */ /* at the front or the back of the */ /* queue. */ DPRINTF((DBG_DEV, "dev_queue_xmit(skb=%X, dev=%X, pri = %d)\n", skb, dev, pri)); if (dev == NULL) { printk("dev.c: dev_queue_xmit: dev = NULL\n"); return; } IS_SKB(skb); skb->dev = dev; if (skb->next != NULL) { /* Make sure we haven't missed an interrupt. */ dev->hard_start_xmit(NULL, dev); return; } if (pri < 0) { pri = -pri-1; where = 1; } if (pri >= DEV_NUMBUFFS) { printk("bad priority in dev_queue_xmit.\n"); pri = 1; } if (dev->hard_start_xmit(skb, dev) == 0) { return; } /* Put skb into a bidirectional circular linked list. */ DPRINTF((DBG_DEV, "dev_queue_xmit dev->buffs[%d]=%X\n", pri, dev->buffs[pri])); /* Interrupts should already be cleared by hard_start_xmit. */ cli(); skb->magic = DEV_QUEUE_MAGIC; if(where) skb_queue_head(&dev->buffs[pri],skb); else skb_queue_tail(&dev->buffs[pri],skb); skb->magic = DEV_QUEUE_MAGIC; sti(); } /* * Receive a packet from a device driver and queue it for the upper * (protocol) levels. It always succeeds. */ void netif_rx(struct sk_buff *skb) { /* Set any necessary flags. */ skb->sk = NULL; skb->free = 1; /* and add it to the "backlog" queue. */ IS_SKB(skb); skb_queue_tail(&backlog,skb); /* If any packet arrived, mark it for processing. */ if (backlog != NULL) mark_bh(INET_BH); return; } /* * The old interface to fetch a packet from a device driver. * This function is the base level entry point for all drivers that * want to send a packet to the upper (protocol) levels. It takes * care of de-multiplexing the packet to the various modules based * on their protocol ID. * * Return values: 1 <- exit I can't do any more * 0 <- feed me more (i.e. "done", "OK"). */ int dev_rint(unsigned char *buff, long len, int flags, struct device *dev) { static int dropping = 0; struct sk_buff *skb = NULL; unsigned char *to; int amount, left; int len2; if (dev == NULL || buff == NULL || len <= 0) return(1); if (flags & IN_SKBUFF) { skb = (struct sk_buff *) buff; } else { if (dropping) { if (backlog != NULL) return(1); printk("INET: dev_rint: no longer dropping packets.\n"); dropping = 0; } skb = alloc_skb(sizeof(*skb) + len, GFP_ATOMIC); if (skb == NULL) { printk("dev_rint: packet dropped on %s (no memory) !\n", dev->name); dropping = 1; return(1); } skb->mem_len = sizeof(*skb) + len; skb->mem_addr = (struct sk_buff *) skb; /* First we copy the packet into a buffer, and save it for later. */ to = skb->data; left = len; len2 = len; while (len2 > 0) { amount = min(len2, (unsigned long) dev->rmem_end - (unsigned long) buff); memcpy(to, buff, amount); len2 -= amount; left -= amount; buff += amount; to += amount; if ((unsigned long) buff == dev->rmem_end) buff = (unsigned char *) dev->rmem_start; } } skb->len = len; skb->dev = dev; skb->free = 1; netif_rx(skb); /* OK, all done. */ return(0); } /* This routine causes all interfaces to try to send some data. */ void dev_transmit(void) { struct device *dev; for (dev = dev_base; dev != NULL; dev = dev->next) { if (!dev->tbusy) { dev_tint(dev); } } } static volatile char in_bh = 0; int in_inet_bh() /* Used by timer.c */ { return(in_bh==0?0:1); } /* * This function gets called periodically, to see if we can * process any data that came in from some interface. * */ void inet_bh(void *tmp) { struct sk_buff *skb; struct packet_type *ptype; unsigned short type; unsigned char flag = 0; int nitcount; /* Atomically check and mark our BUSY state. */ if (set_bit(1, (void*)&in_bh)) return; /* Can we send anything now? */ dev_transmit(); /* Any data left to process? */ while((skb=skb_dequeue(&backlog))!=NULL) { nitcount=dev_nit; flag=0; sti(); /* * Bump the pointer to the next structure. * This assumes that the basic 'skb' pointer points to * the MAC header, if any (as indicated by its "length" * field). Take care now! */ skb->h.raw = skb->data + skb->dev->hard_header_len; skb->len -= skb->dev->hard_header_len; /* * Fetch the packet protocol ID. This is also quite ugly, as * it depends on the protocol driver (the interface itself) to * know what the type is, or where to get it from. The Ethernet * interfaces fetch the ID from the two bytes in the Ethernet MAC * header (the h_proto field in struct ethhdr), but drivers like * SLIP and PLIP have no alternative but to force the type to be * IP or something like that. Sigh- FvK */ type = skb->dev->type_trans(skb, skb->dev); /* * We got a packet ID. Now loop over the "known protocols" * table (which is actually a linked list, but this will * change soon if I get my way- FvK), and forward the packet * to anyone who wants it. */ for (ptype = ptype_base; ptype != NULL; ptype = ptype->next) { if (ptype->type == type || ptype->type == NET16(ETH_P_ALL)) { struct sk_buff *skb2; if (ptype->type==NET16(ETH_P_ALL)) nitcount--; if (ptype->copy || nitcount) { /* copy if we need to */ skb2 = alloc_skb(skb->mem_len, GFP_ATOMIC); if (skb2 == NULL) continue; memcpy(skb2, (const void *) skb, skb->mem_len); skb2->mem_addr = skb2; skb2->h.raw = (unsigned char *)( (unsigned long) skb2 + (unsigned long) skb->h.raw - (unsigned long) skb ); skb2->free = 1; } else { skb2 = skb; } /* This used to be in the 'else' part, but then * we don't have this flag set when we get a * protocol that *does* require copying... -FvK */ flag = 1; /* Kick the protocol handler. */ ptype->func(skb2, skb->dev, ptype); } } /* * That's odd. We got an unknown packet. Who's using * stuff like Novell or Amoeba on this network?? */ if (!flag) { DPRINTF((DBG_DEV, "INET: unknown packet type 0x%04X (ignored)\n", type)); skb->sk = NULL; kfree_skb(skb, FREE_WRITE); } /* Again, see if we can transmit anything now. */ dev_transmit(); cli(); } in_bh = 0; sti(); dev_transmit(); } /* * This routine is called when an device driver (i.e. an * interface) is * ready to transmit a packet. */ void dev_tint(struct device *dev) { int i; struct sk_buff *skb; for(i = 0;i < DEV_NUMBUFFS; i++) { while((skb=skb_dequeue(&dev->buffs[i]))!=NULL) { skb->magic = 0; skb->next = NULL; skb->prev = NULL; dev->queue_xmit(skb,dev,-i - 1); if (dev->tbusy) return; } } } /* Perform a SIOCGIFCONF call. */ static int dev_ifconf(char *arg) { struct ifconf ifc; struct ifreq ifr; struct device *dev; char *pos; int len; int err; /* Fetch the caller's info block. */ err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf)); if(err) return err; memcpy_fromfs(&ifc, arg, sizeof(struct ifconf)); len = ifc.ifc_len; pos = ifc.ifc_buf; /* Loop over the interfaces, and write an info block for each. */ for (dev = dev_base; dev != NULL; dev = dev->next) { if(!(dev->flags & IFF_UP)) continue; memset(&ifr, 0, sizeof(struct ifreq)); strcpy(ifr.ifr_name, dev->name); (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family; (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr; /* Write this block to the caller's space. */ memcpy_tofs(pos, &ifr, sizeof(struct ifreq)); pos += sizeof(struct ifreq); len -= sizeof(struct ifreq); if (len < sizeof(struct ifreq)) break; } /* All done. Write the updated control block back to the caller. */ ifc.ifc_len = (pos - ifc.ifc_buf); ifc.ifc_req = (struct ifreq *) ifc.ifc_buf; memcpy_tofs(arg, &ifc, sizeof(struct ifconf)); return(pos - arg); } /* Print device statistics. */ char *sprintf_stats(char *buffer, struct device *dev) { char *pos = buffer; struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL); if (stats) pos += sprintf(pos, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n", dev->name, stats->rx_packets, stats->rx_errors, stats->rx_dropped + stats->rx_missed_errors, stats->rx_fifo_errors, stats->rx_length_errors + stats->rx_over_errors + stats->rx_crc_errors + stats->rx_frame_errors, stats->tx_packets, stats->tx_errors, stats->tx_dropped, stats->tx_fifo_errors, stats->collisions, stats->tx_carrier_errors + stats->tx_aborted_errors + stats->tx_window_errors + stats->tx_heartbeat_errors); else pos += sprintf(pos, "%6s: No statistics available.\n", dev->name); return pos; } /* Called from the PROCfs module. */ int dev_get_info(char *buffer) { char *pos = buffer; struct device *dev; pos += sprintf(pos, "Inter-| Receive | Transmit\n" " face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n"); for (dev = dev_base; dev != NULL; dev = dev->next) { pos = sprintf_stats(pos, dev); } return pos - buffer; } static inline int bad_mask(unsigned long mask, unsigned long addr) { if (addr & (mask = ~mask)) return 1; mask = ntohl(mask); if (mask & (mask+1)) return 1; return 0; } /* Perform the SIOCxIFxxx calls. */ static int dev_ifsioc(void *arg, unsigned int getset) { struct ifreq ifr; struct device *dev; int ret; /* Fetch the caller's info block. */ int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq)); if(err) return err; memcpy_fromfs(&ifr, arg, sizeof(struct ifreq)); /* See which interface the caller is talking about. */ if ((dev = dev_get(ifr.ifr_name)) == NULL) return(-EINVAL); switch(getset) { case SIOCGIFFLAGS: ifr.ifr_flags = dev->flags; memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); ret = 0; break; case SIOCSIFFLAGS: { int old_flags = dev->flags; dev->flags = ifr.ifr_flags & ( IFF_UP | IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK | IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING | IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI); if ( (old_flags & IFF_PROMISC) && ((dev->flags & IFF_PROMISC) == 0)) dev->set_multicast_list(dev,0,NULL); if ( (dev->flags & IFF_PROMISC) && ((old_flags & IFF_PROMISC) == 0)) dev->set_multicast_list(dev,-1,NULL); if ((old_flags & IFF_UP) && ((dev->flags & IFF_UP) == 0)) { ret = dev_close(dev); } else { ret = (! (old_flags & IFF_UP) && (dev->flags & IFF_UP)) ? dev_open(dev) : 0; if(ret<0) dev->flags&=~IFF_UP; /* Didnt open so down the if */ } } break; case SIOCGIFADDR: (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr; (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family; (*(struct sockaddr_in *) &ifr.ifr_addr).sin_port = 0; memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); ret = 0; break; case SIOCSIFADDR: dev->pa_addr = (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr; dev->family = ifr.ifr_addr.sa_family; dev->pa_mask = get_mask(dev->pa_addr); dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask; ret = 0; break; case SIOCGIFBRDADDR: (*(struct sockaddr_in *) &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr; (*(struct sockaddr_in *) &ifr.ifr_broadaddr).sin_family = dev->family; (*(struct sockaddr_in *) &ifr.ifr_broadaddr).sin_port = 0; memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); ret = 0; break; case SIOCSIFBRDADDR: dev->pa_brdaddr = (*(struct sockaddr_in *) &ifr.ifr_broadaddr).sin_addr.s_addr; ret = 0; break; case SIOCGIFDSTADDR: (*(struct sockaddr_in *) &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr; (*(struct sockaddr_in *) &ifr.ifr_broadaddr).sin_family = dev->family; (*(struct sockaddr_in *) &ifr.ifr_broadaddr).sin_port = 0; memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); ret = 0; break; case SIOCSIFDSTADDR: dev->pa_dstaddr = (*(struct sockaddr_in *) &ifr.ifr_dstaddr).sin_addr.s_addr; ret = 0; break; case SIOCGIFNETMASK: (*(struct sockaddr_in *) &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask; (*(struct sockaddr_in *) &ifr.ifr_netmask).sin_family = dev->family; (*(struct sockaddr_in *) &ifr.ifr_netmask).sin_port = 0; memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); ret = 0; break; case SIOCSIFNETMASK: { unsigned long mask = (*(struct sockaddr_in *) &ifr.ifr_netmask).sin_addr.s_addr; ret = -EINVAL; if (bad_mask(mask,0)) break; dev->pa_mask = mask; ret = 0; break; } case SIOCGIFMETRIC: ifr.ifr_metric = dev->metric; memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); ret = 0; break; case SIOCSIFMETRIC: dev->metric = ifr.ifr_metric; ret = 0; break; case SIOCGIFMTU: ifr.ifr_mtu = dev->mtu; memcpy_tofs(arg, &ifr, sizeof(struct ifreq)); ret = 0; break; case SIOCSIFMTU: dev->mtu = ifr.ifr_mtu; ret = 0; break; case SIOCGIFMEM: printk("NET: ioctl(SIOCGIFMEM, 0x%08X)\n", (int)arg); ret = -EINVAL; break; case SIOCSIFMEM: printk("NET: ioctl(SIOCSIFMEM, 0x%08X)\n", (int)arg); ret = -EINVAL; break; case SIOCGIFHWADDR: memcpy(ifr.ifr_hwaddr,dev->dev_addr, MAX_ADDR_LEN); memcpy_tofs(arg,&ifr,sizeof(struct ifreq)); ret=0; break; default: ret = -EINVAL; } return(ret); } /* This function handles all "interface"-type I/O control requests. */ int dev_ioctl(unsigned int cmd, void *arg) { struct iflink iflink; struct ddi_device *dev; int ret; switch(cmd) { case IP_SET_DEV: printk("Your network configuration program needs upgrading.\n"); return -EINVAL; case SIOCGIFCONF: (void) dev_ifconf((char *) arg); ret = 0; break; case SIOCGIFFLAGS: case SIOCSIFFLAGS: case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFDSTADDR: case SIOCSIFDSTADDR: case SIOCGIFBRDADDR: case SIOCSIFBRDADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: case SIOCGIFMETRIC: case SIOCSIFMETRIC: case SIOCGIFMTU: case SIOCSIFMTU: case SIOCGIFMEM: case SIOCSIFMEM: case SIOCGIFHWADDR: if (!suser()) return(-EPERM); ret = dev_ifsioc(arg, cmd); break; case SIOCSIFLINK: if (!suser()) return(-EPERM); memcpy_fromfs(&iflink, arg, sizeof(iflink)); dev = ddi_map(iflink.id); if (dev == NULL) return(-EINVAL); /* Now allocate an interface and connect it. */ printk("AF_INET: DDI \"%s\" linked to stream \"%s\"\n", dev->name, iflink.stream); ret = 0; break; default: ret = -EINVAL; } return(ret); } /* Initialize the DEV module. */ void dev_init(void) { struct device *dev, *dev2; /* Add the devices. * If the call to dev->init fails, the dev is removed * from the chain disconnecting the device until the * next reboot. */ dev2 = NULL; for (dev = dev_base; dev != NULL; dev=dev->next) { if (dev->init && dev->init(dev)) { if (dev2 == NULL) dev_base = dev->next; else dev2->next = dev->next; } else { dev2 = dev; } } /* Set up some IP addresses. */ ip_bcast = in_aton("255.255.255.255"); }