📄 af_ipx.c
字号:
datalink = pSNAP_datalink; break; case IPX_FRAME_8023: dlink_type = htons(ETH_P_802_3); datalink = p8023_datalink; break; case IPX_FRAME_NONE: default: break; } err = -ENETDOWN; if(!(dev->flags & IFF_UP)) goto out_dev; /* Check addresses are suitable */ err = -EINVAL; if(dev->addr_len > IPX_NODE_LEN) goto out_dev; err = -EPROTONOSUPPORT; if(datalink == NULL) goto out_dev; if((intrfc = ipxitf_find_using_phys(dev, dlink_type)) == NULL) { /* Ok now create */ intrfc = (ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC); err = -EAGAIN; if(intrfc == NULL) goto out_dev; intrfc->if_dev = dev; intrfc->if_netnum = idef->ipx_network; intrfc->if_dlink_type = dlink_type; intrfc->if_dlink = datalink; intrfc->if_sklist = NULL; intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET; /* Setup primary if necessary */ if((idef->ipx_special == IPX_PRIMARY)) ipx_primary_net = intrfc; intrfc->if_internal = 0; intrfc->if_ipx_offset = dev->hard_header_len + datalink->header_length; if(memcmp(idef->ipx_node, "\000\000\000\000\000\000", IPX_NODE_LEN) == 0) { memset(intrfc->if_node, 0, IPX_NODE_LEN); memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]), dev->dev_addr, dev->addr_len); } else memcpy(intrfc->if_node, idef->ipx_node, IPX_NODE_LEN); spin_lock_init(&intrfc->if_sklist_lock); atomic_set(&intrfc->refcnt, 1); MOD_INC_USE_COUNT; ipxitf_hold(intrfc); ipxitf_insert(intrfc); } /* If the network number is known, add a route */ err = 0; if(!intrfc->if_netnum) goto out_intrfc; err = ipxitf_add_local_route(intrfc);out_intrfc: ipxitf_put(intrfc); return err;out_dev: dev_put(dev); return err;}static int ipxitf_delete(ipx_interface_definition *idef){ struct net_device *dev = NULL; unsigned short dlink_type = 0; ipx_interface *intrfc; int ret = 0; spin_lock_bh(&ipx_interfaces_lock); if(idef->ipx_special == IPX_INTERNAL) { if(ipx_internal_net != NULL) { __ipxitf_put(ipx_internal_net); goto out; } ret = -ENOENT; goto out; } dlink_type = ipx_map_frame_type(idef->ipx_dlink_type); if(dlink_type == 0) { ret = -EPROTONOSUPPORT; goto out; } dev = __dev_get_by_name(idef->ipx_device); if(dev == NULL) { ret = -ENODEV; goto out; } intrfc = __ipxitf_find_using_phys(dev, dlink_type); if(intrfc != NULL) __ipxitf_put(intrfc); else ret = -EINVAL;out: spin_unlock_bh(&ipx_interfaces_lock); return ret;}static ipx_interface *ipxitf_auto_create(struct net_device *dev, unsigned short dlink_type){ struct datalink_proto *datalink = NULL; ipx_interface *intrfc; switch(htons(dlink_type)) { case ETH_P_IPX: datalink = pEII_datalink; break; case ETH_P_802_2: datalink = p8022_datalink; break; case ETH_P_SNAP: datalink = pSNAP_datalink; break; case ETH_P_802_3: datalink = p8023_datalink; break; default: return (NULL); } if(dev == NULL) return (NULL); /* Check addresses are suitable */ if(dev->addr_len>IPX_NODE_LEN) return (NULL); intrfc = (ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC); if(intrfc != NULL) { intrfc->if_dev = dev; intrfc->if_netnum = 0; intrfc->if_dlink_type = dlink_type; intrfc->if_dlink = datalink; intrfc->if_sklist = NULL; intrfc->if_internal = 0; intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET; intrfc->if_ipx_offset = dev->hard_header_len + datalink->header_length; memset(intrfc->if_node, 0, IPX_NODE_LEN); memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]), dev->dev_addr, dev->addr_len); spin_lock_init(&intrfc->if_sklist_lock); atomic_set(&intrfc->refcnt, 1); MOD_INC_USE_COUNT; ipxitf_insert(intrfc); } return (intrfc);}static int ipxitf_ioctl(unsigned int cmd, void *arg){ struct ifreq ifr; int err, val; switch(cmd) { case SIOCSIFADDR: { struct sockaddr_ipx *sipx; ipx_interface_definition f; if(copy_from_user(&ifr, arg, sizeof(ifr))) return (-EFAULT); sipx = (struct sockaddr_ipx *)&ifr.ifr_addr; if(sipx->sipx_family != AF_IPX) return (-EINVAL); f.ipx_network = sipx->sipx_network; memcpy(f.ipx_device,ifr.ifr_name,sizeof(f.ipx_device)); memcpy(f.ipx_node, sipx->sipx_node, IPX_NODE_LEN); f.ipx_dlink_type = sipx->sipx_type; f.ipx_special = sipx->sipx_special; if(sipx->sipx_action == IPX_DLTITF) return (ipxitf_delete(&f)); else return (ipxitf_create(&f)); } case SIOCGIFADDR: { struct sockaddr_ipx *sipx; ipx_interface *ipxif; struct net_device *dev; if(copy_from_user(&ifr, arg, sizeof(ifr))) return (-EFAULT); sipx = (struct sockaddr_ipx *)&ifr.ifr_addr; dev = __dev_get_by_name(ifr.ifr_name); if(!dev) return (-ENODEV); ipxif = ipxitf_find_using_phys(dev, ipx_map_frame_type(sipx->sipx_type)); if(ipxif == NULL) return (-EADDRNOTAVAIL); sipx->sipx_family = AF_IPX; sipx->sipx_network = ipxif->if_netnum; memcpy(sipx->sipx_node, ipxif->if_node, sizeof(sipx->sipx_node)); err = -EFAULT; if(!copy_to_user(arg, &ifr, sizeof(ifr))) err = 0; ipxitf_put(ipxif); return (err); } case SIOCAIPXITFCRT: { err = get_user(val, (unsigned char *) arg); if(err) return (err); return (ipxcfg_set_auto_create(val)); } case SIOCAIPXPRISLT: { err = get_user(val, (unsigned char *) arg); if(err) return (err); return (ipxcfg_set_auto_select(val)); } default: return (-EINVAL); }}/**************************************************************************\* ** Routing tables for the IPX socket layer. ** *\**************************************************************************/static ipx_route *ipxrtr_lookup(__u32 net){ ipx_route *r; read_lock_bh(&ipx_routes_lock); for(r = ipx_routes; (r != NULL) && (r->ir_net != net); r = r->ir_next) ; read_unlock_bh(&ipx_routes_lock); return (r);}/* caller must hold a reference to intrfc */static int ipxrtr_add_route(__u32 network, ipx_interface *intrfc, unsigned char *node){ ipx_route *rt; /* Get a route structure; either existing or create */ rt = ipxrtr_lookup(network); if(rt == NULL) { rt = (ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC); if(rt == NULL) return (-EAGAIN); write_lock_bh(&ipx_routes_lock); rt->ir_next = ipx_routes; ipx_routes = rt; write_unlock_bh(&ipx_routes_lock); } else if(intrfc == ipx_internal_net) return (-EEXIST); rt->ir_net = network; rt->ir_intrfc = intrfc; if(node == NULL) { memset(rt->ir_router_node, '\0', IPX_NODE_LEN); rt->ir_routed = 0; } else { memcpy(rt->ir_router_node, node, IPX_NODE_LEN); rt->ir_routed = 1; } return (0);}static void ipxrtr_del_routes(ipx_interface *intrfc){ ipx_route **r, *tmp; write_lock_bh(&ipx_routes_lock); for(r = &ipx_routes; (tmp = *r) != NULL;) { if(tmp->ir_intrfc == intrfc) { *r = tmp->ir_next; kfree(tmp); } else r = &(tmp->ir_next); } write_unlock_bh(&ipx_routes_lock);}static int ipxrtr_create(ipx_route_definition *rd){ ipx_interface *intrfc; int ret; /* Find the appropriate interface */ intrfc = ipxitf_find_using_net(rd->ipx_router_network); if(intrfc == NULL) return (-ENETUNREACH); ret = ipxrtr_add_route(rd->ipx_network, intrfc, rd->ipx_router_node); ipxitf_put(intrfc); return ret;}static int ipxrtr_delete(long net){ ipx_route **r; ipx_route *tmp; int err; write_lock_bh(&ipx_routes_lock); for(r = &ipx_routes; (tmp = *r) != NULL;) { if(tmp->ir_net == net) { /* Directly connected; can't lose route */ err = -EPERM; if(!(tmp->ir_routed)) goto out; *r = tmp->ir_next; kfree(tmp); err = 0; goto out; } r = &(tmp->ir_next); } err = -ENOENT;out: write_unlock_bh(&ipx_routes_lock); return err;}/* * Checksum routine for IPX */ /* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */static __u16 ipx_set_checksum(struct ipxhdr *packet,int length) { /* * NOTE: sum is a net byte order quantity, which optimizes the * loop. This only works on big and little endian machines. (I * don't know of a machine that isn't.) */ __u32 sum = 0; /* Pointer to second word - We skip the checksum field */ __u16 *p = (__u16 *)&packet->ipx_pktsize; /* Number of complete words */ __u32 i = length >> 1; char hops = packet->ipx_tctrl; /* Hop count excluded from checksum calc */ packet->ipx_tctrl = 0; /* Loop through all complete words except the checksum field */ while(--i) sum += *p++; /* Add on the last part word if it exists */ if(packet->ipx_pktsize & htons(1)) sum += ntohs(0xff00) & *p; packet->ipx_tctrl = hops; /* Do final fixup */ sum = (sum & 0xffff) + (sum >> 16); /* It's a pity there's no concept of carry in C */ if(sum >= 0x10000) sum++; return (~sum);}/* * Route an outgoing frame from a socket. */static int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, struct iovec *iov, int len, int noblock){ struct sk_buff *skb; ipx_interface *intrfc; struct ipxhdr *ipx; int size; int ipx_offset; ipx_route *rt = NULL; int err; /* Find the appropriate interface on which to send packet */ if(!usipx->sipx_network && (ipx_primary_net != NULL)) { usipx->sipx_network = ipx_primary_net->if_netnum; intrfc = ipx_primary_net; } else { rt = ipxrtr_lookup(usipx->sipx_network); if(rt == NULL) return (-ENETUNREACH); intrfc = rt->ir_intrfc; } ipxitf_hold(intrfc); ipx_offset = intrfc->if_ipx_offset; size = sizeof(struct ipxhdr) + len; size += ipx_offset; skb = sock_alloc_send_skb(sk, size, 0, noblock, &err); if(skb == NULL) goto out; skb_reserve(skb,ipx_offset); skb->sk = sk; /* Fill in IPX header */ ipx = (struct ipxhdr *)skb_put(skb, sizeof(struct ipxhdr)); ipx->ipx_pktsize= htons(len + sizeof(struct ipxhdr)); ipx->ipx_tctrl = 0; ipx->ipx_type = usipx->sipx_type; skb->h.raw = (void *)skb->nh.ipxh = ipx; ipx->ipx_source.net = sk->protinfo.af_ipx.intrfc->if_netnum;#ifdef CONFIG_IPX_INTERN memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.node, IPX_NODE_LEN);#else if((err = ntohs(sk->protinfo.af_ipx.port)) == 0x453 || err == 0x452) { /* RIP/SAP special handling for mars_nwe */ ipx->ipx_source.net = intrfc->if_netnum; memcpy(ipx->ipx_source.node, intrfc->if_node, IPX_NODE_LEN); } else { ipx->ipx_source.net = sk->protinfo.af_ipx.intrfc->if_netnum; memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN); }#endif /* CONFIG_IPX_INTERN */ ipx->ipx_source.sock = sk->protinfo.af_ipx.port; ipx->ipx_dest.net = usipx->sipx_network; memcpy(ipx->ipx_dest.node,usipx->sipx_node,IPX_NODE_LEN); ipx->ipx_dest.sock = usipx->sipx_port; err = memcpy_fromiovec(skb_put(skb,len),iov,len); if(err) { kfree_skb(skb); goto out; } /* Apply checksum. Not allowed on 802.3 links. */ if(sk->no_check || intrfc->if_dlink_type == IPX_FRAME_8023) ipx->ipx_checksum=0xFFFF; else ipx->ipx_checksum = ipx_set_checksum(ipx, len + sizeof(struct ipxhdr)); err = ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ? rt->ir_router_node : ipx->ipx_dest.node);out: ipxitf_put(intrfc); return err;} int ipxrtr_route_skb(struct sk_buff *skb){ struct ipxhdr *ipx = skb->nh.ipxh; ipx_route *r; r = ipxrtr_lookup(ipx->ipx_dest.net); if(r == NULL) /* no known route */ { kfree_skb(skb); return (0); } ipxitf_hold(r->ir_intrfc); (void)ipxitf_send(r->ir_intrfc, skb, (r->ir_routed) ? r->ir_router_node : ipx->ipx_dest.node); ipxitf_put(r->ir_intrfc); return (0);}/* * We use a normal struct rtentry for route handling */static int ipxrtr_ioctl(unsigned int cmd, void *arg){ struct rtentry rt; /* Use these to behave like 'other' stacks */ struct sockaddr_ipx *sg,*st; int err; err = copy_from_user(&rt,arg,sizeof(rt)); if(err) return (-EFAULT);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -