static int econet_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; struct sockaddr_ec *saddr=(struct sockaddr_ec *)msg->msg_name; struct device *dev; struct ec_addr addr; struct ec_device *edev; int err; unsigned char port, cb; struct sk_buff *skb; struct ec_cb *eb; #ifdef CONFIG_ECONET_NATIVE unsigned short proto = 0; #endif #ifdef CONFIG_ECONET_AUNUDP struct msghdr udpmsg; struct iovec iov[msg->msg_iovlen+1]; struct aunhdr ah; struct sockaddr_in udpdest; __kernel_size_t size; int i; mm_segment_t oldfs; #endif /* * Check the flags. */ if (msg->msg_flags&~MSG_DONTWAIT) return(-EINVAL); /* * Get and verify the address. */ if (saddr == NULL) { addr.station = sk->protinfo.af_econet->station; addr.net = sk->protinfo.af_econet->net; port = sk->protinfo.af_econet->port; cb = sk->protinfo.af_econet->cb; } else { if (msg->msg_namelen < sizeof(struct sockaddr_ec)) return -EINVAL; addr.station = saddr->addr.station; addr.net = saddr->addr.net; port = saddr->port; cb = saddr->cb; } /* Look for a device with the right network number. */ for (edev = edevlist; edev && (edev->net != addr.net); edev = edev->next); /* Bridge? What's that? */ if (edev == NULL) return -ENETUNREACH; dev = edev->dev; if (dev->type == ARPHRD_ECONET) { /* Real hardware Econet. We're not worthy etc. */ #ifdef CONFIG_ECONET_NATIVE dev_lock_list(); skb = sock_alloc_send_skb(sk, len+dev->hard_header_len+15, 0, msg->msg_flags & MSG_DONTWAIT, &err); if (skb==NULL) goto out_unlock; skb_reserve(skb, (dev->hard_header_len+15)&~15); skb->nh.raw = skb->data; eb = (struct ec_cb *)&skb->cb; eb->cookie = saddr->cookie; eb->sec = *saddr; eb->sent = ec_tx_done; if (dev->hard_header) { int res; err = -EINVAL; res = dev->hard_header(skb, dev, ntohs(proto), &addr, NULL, len); if (sock->type != SOCK_DGRAM) { skb->tail = skb->data; skb->len = 0; } else if (res < 0) goto out_free; } /* Copy the data. Returns -EFAULT on error */ err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len); skb->protocol = proto; skb->dev = dev; skb->priority = sk->priority; if (err) goto out_free; err = -ENETDOWN; if (!(dev->flags & IFF_UP)) goto out_free; /* * Now send it */ dev_unlock_list(); dev_queue_xmit(skb); return(len); out_free: kfree_skb(skb); out_unlock: dev_unlock_list(); #else err = -EPROTOTYPE; #endif return err; } #ifdef CONFIG_ECONET_AUNUDP /* AUN virtual Econet. */ if (udpsock == NULL) return -ENETDOWN; /* No socket - can't send */ /* Make up a UDP datagram and hand it off to some higher intellect. */ memset(&udpdest, 0, sizeof(udpdest)); udpdest.sin_family = AF_INET; udpdest.sin_port = htons(AUN_PORT); /* At the moment we use the stupid Acorn scheme of Econet address y.x maps to IP a.b.c.x. This should be replaced with something more flexible and more aware of subnet masks. */ { struct in_device *idev = (struct in_device *)dev->ip_ptr; unsigned long network = ntohl(idev->ifa_list->ifa_address) & 0xffffff00; /* !!! */ udpdest.sin_addr.s_addr = htonl(network | addr.station); } ah.port = port; ah.cb = cb & 0x7f; ah.code = 2; /* magic */ ah.pad = 0; /* tack our header on the front of the iovec */ size = sizeof(struct aunhdr); iov[0].iov_base = (void *)&ah; iov[0].iov_len = size; for (i = 0; i < msg->msg_iovlen; i++) { void *base = msg->msg_iov[i].iov_base; size_t len = msg->msg_iov[i].iov_len; /* Check it now since we switch to KERNEL_DS later. */ if ((err = verify_area(VERIFY_READ, base, len)) < 0) return err; iov[i+1].iov_base = base; iov[i+1].iov_len = len; size += len; } /* Get a skbuff (no data, just holds our cb information) */ if ((skb = sock_alloc_send_skb(sk, 0, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) return err; eb = (struct ec_cb *)&skb->cb; eb->cookie = saddr->cookie; eb->timeout = (5*HZ); eb->start = jiffies; ah.handle = aun_seq; eb->seq = (aun_seq++); eb->sec = *saddr; skb_queue_tail(&aun_queue, skb); udpmsg.msg_name = (void *)&udpdest; udpmsg.msg_namelen = sizeof(udpdest); udpmsg.msg_iov = &iov[0]; udpmsg.msg_iovlen = msg->msg_iovlen + 1; udpmsg.msg_control = NULL; udpmsg.msg_controllen = 0; udpmsg.msg_flags=0; oldfs = get_fs(); set_fs(KERNEL_DS); /* More privs :-) */ err = sock_sendmsg(udpsock, &udpmsg, size); set_fs(oldfs); #else err = -EPROTOTYPE; #endif return err; }
static int packet_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) { char name[15]; struct device *dev; /* * Check legality */ if(addr_len!=sizeof(struct sockaddr)) return -EINVAL; strncpy(name,uaddr->sa_data,14); name[14]=0; /* * Lock the device chain while we sanity check * the bind request. */ dev_lock_list(); dev=dev_get(name); if(dev==NULL) { dev_unlock_list(); return -ENODEV; } if(!(dev->flags&IFF_UP)) { dev_unlock_list(); return -ENETDOWN; } /* * Perform the request. */ memcpy(sk->protinfo.af_packet.device_name,name,15); /* * Rewrite an existing hook if present. */ if(sk->protinfo.af_packet.prot_hook) { dev_remove_pack(sk->protinfo.af_packet.prot_hook); sk->protinfo.af_packet.prot_hook->dev=dev; sk->protinfo.af_packet.bound_dev=dev; dev_add_pack(sk->protinfo.af_packet.prot_hook); } else { int err=packet_attach(sk, dev); if(err) { dev_unlock_list(); return err; } } /* * Now the notifier is set up right this lot is safe. */ dev_unlock_list(); return 0; }
int ip_build_xmit(struct sock *sk, void getfrag (const void *, __u32, char *, unsigned int, unsigned int), const void *frag, unsigned short int length, __u32 daddr, __u32 user_saddr, struct options * opt, int flags, int type, int noblock) { struct rtable *rt; unsigned int fraglen, maxfraglen, fragheaderlen; int offset, mf; __u32 saddr; unsigned short id; struct iphdr *iph; __u32 raddr; struct device *dev = NULL; struct hh_cache * hh=NULL; int nfrags=0; __u32 true_daddr = daddr; if (opt && opt->srr && !sk->ip_hdrincl) daddr = opt->faddr; ip_statistics.IpOutRequests++; #ifdef CONFIG_IP_MULTICAST if(MULTICAST(daddr) && *sk->ip_mc_name) { dev=dev_get(sk->ip_mc_name); if(!dev) return -ENODEV; rt=NULL; if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr))) saddr = sk->saddr; else saddr = dev->pa_addr; } else { #endif rt = ip_check_route(&sk->ip_route_cache, daddr, sk->localroute || (flags&MSG_DONTROUTE) || (opt && opt->is_strictroute), sk->bound_device); if (rt == NULL) { ip_statistics.IpOutNoRoutes++; return(-ENETUNREACH); } saddr = rt->rt_src; hh = rt->rt_hh; if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr))) saddr = sk->saddr; dev=rt->rt_dev; #ifdef CONFIG_IP_MULTICAST } if (rt && !dev) dev = rt->rt_dev; #endif if (user_saddr) saddr = user_saddr; raddr = rt ? rt->rt_gateway : daddr; /* * Now compute the buffer space we require */ /* * Try the simple case first. This leaves broadcast, multicast, fragmented frames, and by * choice RAW frames within 20 bytes of maximum size(rare) to the long path */ if (!sk->ip_hdrincl) { length += sizeof(struct iphdr); if (opt) { /* make sure not to exceed maximum packet size */ if (0xffff - length < opt->optlen) return -EMSGSIZE; length += opt->optlen; } } if(length <= dev->mtu && !MULTICAST(daddr) && daddr!=0xFFFFFFFF && daddr!=dev->pa_brdaddr) { int error; struct sk_buff *skb=sock_alloc_send_skb(sk, length+15+dev->hard_header_len,0, noblock, &error); if(skb==NULL) { ip_statistics.IpOutDiscards++; return error; } skb->dev=dev; skb->protocol = htons(ETH_P_IP); skb->free=1; skb->when=jiffies; skb->sk=sk; skb->arp=0; skb->saddr=saddr; skb->raddr = raddr; skb_reserve(skb,(dev->hard_header_len+15)&~15); if (hh) { skb->arp=1; memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len); if (!hh->hh_uptodate) { skb->arp = 0; #if RT_CACHE_DEBUG >= 2 printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway); #endif } } else if(dev->hard_header) { if(dev->hard_header(skb,dev,ETH_P_IP,NULL,NULL,0)>0) skb->arp=1; } else skb->arp=1; skb->ip_hdr=iph=(struct iphdr *)skb_put(skb,length); dev_lock_list(); if(!sk->ip_hdrincl) { iph->version=4; iph->ihl=5; iph->tos=sk->ip_tos; iph->tot_len = htons(length); iph->id=htons(ip_id_count++); iph->frag_off = 0; iph->ttl=sk->ip_ttl; iph->protocol=type; iph->saddr=saddr; iph->daddr=daddr; if (opt) { iph->ihl += opt->optlen>>2; ip_options_build(skb, opt, true_daddr, dev->pa_addr, 0); } iph->check=0; iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); getfrag(frag,saddr,((char *)iph)+iph->ihl*4,0, length-iph->ihl*4); }