| /* |
| * 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, <bir7@leland.Stanford.Edu> |
| * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> |
| * Mark Evans, <evansmp@uhura.aston.ac.uk> |
| * |
| * 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. |
| * |
| * 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 <asm/segment.h> |
| #include <asm/system.h> |
| #include <asm/bitops.h> |
| #include <linux/config.h> |
| #include <linux/types.h> |
| #include <linux/kernel.h> |
| #include <linux/sched.h> |
| #include <linux/string.h> |
| #include <linux/mm.h> |
| #include <linux/socket.h> |
| #include <linux/sockios.h> |
| #include <linux/in.h> |
| #include <linux/errno.h> |
| #include <linux/interrupt.h> |
| #include <linux/if_ether.h> |
| #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" |
| |
| |
| static struct packet_type arp_packet_type = { |
| NET16(ETH_P_ARP), |
| 0, /* copy */ |
| arp_rcv, |
| NULL, |
| NULL /* next */ |
| }; |
| |
| |
| 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->pa_addr == 0) |
| { |
| if(dev->flags&IFF_PROMISC) /* This allows all addresses through */ |
| return(IS_MYADDR); |
| else |
| continue; |
| } |
| |
| /* 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); |
| } |
| } |
| } |
| |
| DPRINTF((DBG_DEV, "NONE\n")); |
| |
| if ((addr & 0xFF) == 0xFF) |
| { |
| /* Wrong subnetted IS_BROADCAST */ |
| return(IS_INVBCAST); |
| } |
| 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); |
| } |
| |
| |
| /* 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; |
| |
| /* 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; |
| } |
| } |
| 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 == 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) { |
| 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) && (dev->flags & IFF_POINTOPOINT) && |
| (addr == dev->pa_dstaddr)) |
| return dev; |
| for (dev = dev_base; dev; dev = dev->next) |
| if ((dev->flags & IFF_UP) && !(dev->flags & IFF_POINTOPOINT) && |
| (dev->flags & IFF_LOOPBACK ? (addr == dev->pa_addr) : |
| (dev->pa_mask & addr) == (dev->pa_addr & dev->pa_mask))) |
| break; |
| /* no need to check broadcast addresses */ |
| return dev; |
| } |
| |
| |
| /* 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(ct<DEV_NUMBUFFS) |
| { |
| struct sk_buff *skb; |
| while((skb=skb_dequeue(&dev->buffs[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 = (unsigned char *) (skb + 1); |
| 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; |
| |
| |
| /* 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) |
| { |
| 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 = (unsigned char *) (skb + 1) + 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 |
| * FIXME: Ethernet drivers need potty training in 802.3 packets -AC |
| */ |
| 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) { |
| struct sk_buff *skb2; |
| |
| if (ptype->copy) { /* 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; |
| |
| /* Fetch the caller's info block. */ |
| verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf)); |
| 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; |
| } |
| |
| /* 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. */ |
| verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq)); |
| 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; |
| } |
| 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: |
| dev->pa_mask = (*(struct sockaddr_in *) |
| &ifr.ifr_netmask).sin_addr.s_addr; |
| 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: |
| { /* Maintain backwards-compatibility, to be deleted for 1.00. */ |
| struct device *dev; |
| /* The old 'struct ip_config'. */ |
| struct ip_config { |
| char name[MAX_IP_NAME]; |
| unsigned long paddr, router, net,up:1,destroy:1; |
| } ipc; |
| int retval, loopback; |
| |
| printk("INET: Warning: old-style ioctl(IP_SET_DEV) called!\n"); |
| if (!suser()) |
| return (-EPERM); |
| |
| verify_area (VERIFY_WRITE, arg, sizeof (ipc)); |
| memcpy_fromfs(&ipc, arg, sizeof (ipc)); |
| ipc.name[MAX_IP_NAME-1] = 0; |
| loopback = (strcmp(ipc.name, "loopback") == 0); |
| dev = dev_get( loopback ? "lo" : ipc.name); |
| if (dev == NULL) |
| return -EINVAL; |
| ipc.destroy = 0; |
| dev->pa_addr = ipc.paddr; |
| dev->family = AF_INET; |
| dev->pa_mask = get_mask(dev->pa_addr); |
| dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask; |
| if (ipc.net != 0xffffffff) { |
| dev->flags |= IFF_BROADCAST; |
| dev->pa_brdaddr = ipc.net; |
| } |
| |
| /* To be proper we should delete the route here. */ |
| if (ipc.up == 0) |
| return (dev->flags & IFF_UP != 0) ? dev_close(dev) : 0; |
| |
| if ((dev->flags & IFF_UP) == 0 |
| && (retval = dev_open(dev)) != 0) |
| return retval; |
| printk("%s: adding HOST route of %8.8lx.\n", dev->name, |
| htonl(ipc.paddr)); |
| rt_add(RTF_HOST, ipc.paddr, 0, dev); |
| if (ipc.router != 0 && ipc.router != -1) { |
| rt_add(RTF_GATEWAY, ipc.paddr, ipc.router, dev); |
| printk("%s: adding GATEWAY route of %8.8lx.\n", |
| dev->name, htonl(ipc.paddr)); |
| |
| } |
| return 0; |
| } |
| 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"); |
| } |