| /* |
| * 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. |
| * |
| * PACKET - implements raw packet sockets. |
| * |
| * Version: @(#)packet.c 1.0.6 05/25/93 |
| * |
| * Authors: Ross Biro, <bir7@leland.Stanford.Edu> |
| * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> |
| * |
| * 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 <linux/types.h> |
| #include <linux/sched.h> |
| #include <linux/fcntl.h> |
| #include <linux/socket.h> |
| #include <linux/in.h> |
| #include "inet.h" |
| #include "timer.h" |
| #include "dev.h" |
| #include "ip.h" |
| #include "protocol.h" |
| #include "tcp.h" |
| #include "skbuff.h" |
| #include "sock.h" |
| #include <linux/errno.h> |
| #include <linux/timer.h> |
| #include <asm/system.h> |
| #include <asm/segment.h> |
| #include "udp.h" |
| #include "raw.h" |
| |
| |
| static unsigned long |
| min(unsigned long a, unsigned long b) |
| { |
| if (a < b) return(a); |
| return(b); |
| } |
| |
| |
| /* This should be the easiest of all, all we do is copy it into a buffer. */ |
| int |
| packet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) |
| { |
| struct sock *sk; |
| |
| sk = (struct sock *) pt->data; |
| skb->dev = dev; |
| skb->len += dev->hard_header_len; |
| |
| /* Now see if we are in use. */ |
| cli(); |
| if (sk->inuse) { |
| sti(); |
| /* |
| * Drop any packets if we can't currently deal with |
| * them. Assume that the other end will retransmit |
| * if it was important. |
| */ |
| skb->sk = NULL; |
| kfree_skb(skb, FREE_READ); |
| return(0); |
| } |
| sk->inuse = 1; |
| sti(); |
| skb->sk = sk; |
| |
| /* Charge it too the socket. */ |
| if (sk->rmem_alloc + skb->mem_len >= SK_RMEM_MAX) { |
| skb->sk = NULL; |
| kfree_skb(skb, FREE_READ); |
| return(0); |
| } |
| sk->rmem_alloc += skb->mem_len; |
| |
| /* Now just put it onto the queue. */ |
| if (sk->rqueue == NULL) { |
| sk->rqueue = skb; |
| skb->next = skb; |
| skb->prev = skb; |
| } else { |
| skb->next = sk->rqueue; |
| skb->prev = sk->rqueue->prev; |
| skb->prev->next = skb; |
| skb->next->prev = skb; |
| } |
| wake_up(sk->sleep); |
| release_sock(sk); |
| return(0); |
| } |
| |
| |
| /* This will do terrible things if len + ipheader + devheader > dev->mtu */ |
| static int |
| packet_sendto(struct sock *sk, unsigned char *from, int len, |
| int noblock, unsigned flags, struct sockaddr_in *usin, |
| int addr_len) |
| { |
| struct sk_buff *skb; |
| struct device *dev; |
| struct sockaddr saddr; |
| |
| /* Check the flags. */ |
| if (flags) return(-EINVAL); |
| if (len < 0) return(-EINVAL); |
| |
| /* Get and verify the address. */ |
| if (usin) { |
| if (addr_len < sizeof(saddr)) return(-EINVAL); |
| /* verify_area(VERIFY_WRITE, usin, sizeof(saddr));*/ |
| memcpy_fromfs(&saddr, usin, sizeof(saddr)); |
| } else |
| return(-EINVAL); |
| |
| skb = (struct sk_buff *) sk->prot->wmalloc(sk, len+sizeof(*skb), 0, GFP_KERNEL); |
| |
| /* This shouldn't happen, but it could. */ |
| if (skb == NULL) { |
| DPRINTF((DBG_PKT, "packet_sendto: write buffer full?\n")); |
| return(-EAGAIN); |
| } |
| skb->lock = 0; |
| skb->mem_addr = skb; |
| skb->mem_len = len + sizeof(*skb); |
| skb->sk = sk; |
| skb->free = 1; |
| saddr.sa_data[13] = 0; |
| dev = dev_get(saddr.sa_data); |
| if (dev == NULL) { |
| sk->prot->wfree(sk, skb->mem_addr, skb->mem_len); |
| return(-ENXIO); |
| } |
| /* verify_area(VERIFY_WRITE, from, len);*/ |
| memcpy_fromfs (skb+1, from, len); |
| skb->len = len; |
| skb->next = NULL; |
| if (dev->flags & IFF_UP) dev->queue_xmit(skb, dev, sk->priority); |
| else kfree_skb(skb, FREE_WRITE); |
| return(len); |
| } |
| |
| |
| static int |
| packet_write(struct sock *sk, unsigned char *buff, |
| int len, int noblock, unsigned flags) |
| { |
| return(packet_sendto(sk, buff, len, noblock, flags, NULL, 0)); |
| } |
| |
| |
| static void |
| packet_close(struct sock *sk, int timeout) |
| { |
| sk->inuse = 1; |
| sk->state = TCP_CLOSE; |
| dev_remove_pack((struct packet_type *)sk->pair); |
| kfree_s((void *)sk->pair, sizeof(struct packet_type)); |
| sk->pair = NULL; |
| release_sock(sk); |
| } |
| |
| |
| static int |
| packet_init(struct sock *sk) |
| { |
| struct packet_type *p; |
| |
| p = (struct packet_type *) kmalloc(sizeof(*p), GFP_KERNEL); |
| if (p == NULL) return(-ENOMEM); |
| |
| p->func = packet_rcv; |
| p->type = sk->num; |
| p->data = (void *)sk; |
| dev_add_pack(p); |
| |
| /* We need to remember this somewhere. */ |
| sk->pair = (struct sock *)p; |
| |
| return(0); |
| } |
| |
| |
| /* |
| * This should be easy, if there is something there |
| * we return it, otherwise we block. |
| */ |
| int |
| packet_recvfrom(struct sock *sk, unsigned char *to, int len, |
| int noblock, unsigned flags, struct sockaddr_in *sin, |
| int *addr_len) |
| { |
| int copied=0; |
| struct sk_buff *skb; |
| struct sockaddr *saddr; |
| |
| saddr = (struct sockaddr *)sin; |
| if (len == 0) return(0); |
| if (len < 0) return(-EINVAL); |
| |
| if (sk->shutdown & RCV_SHUTDOWN) return(0); |
| if (addr_len) { |
| verify_area(VERIFY_WRITE, addr_len, sizeof(*addr_len)); |
| put_fs_long(sizeof(*saddr), addr_len); |
| } |
| sk->inuse = 1; |
| while (sk->rqueue == NULL) { |
| if (noblock) { |
| release_sock(sk); |
| return(-EAGAIN); |
| } |
| release_sock(sk); |
| cli(); |
| if (sk->rqueue == NULL) { |
| interruptible_sleep_on(sk->sleep); |
| if (current->signal & ~current->blocked) { |
| return(-ERESTARTSYS); |
| } |
| } |
| sk->inuse = 1; |
| sti(); |
| } |
| skb = sk->rqueue; |
| |
| if (!(flags & MSG_PEEK)) { |
| if (skb->next == skb) { |
| sk->rqueue = NULL; |
| } else { |
| sk->rqueue = (struct sk_buff *)sk->rqueue ->next; |
| skb->prev->next = skb->next; |
| skb->next->prev = skb->prev; |
| } |
| } |
| copied = min(len, skb->len); |
| verify_area(VERIFY_WRITE, to, copied); |
| memcpy_tofs(to, skb+1, copied); |
| |
| /* Copy the address. */ |
| if (saddr) { |
| struct sockaddr addr; |
| |
| addr.sa_family = skb->dev->type; |
| memcpy(addr.sa_data,skb->dev->name, 14); |
| verify_area(VERIFY_WRITE, saddr, sizeof(*saddr)); |
| memcpy_tofs(saddr, &addr, sizeof(*saddr)); |
| } |
| |
| if (!(flags & MSG_PEEK)) { |
| kfree_skb(skb, FREE_READ); |
| } |
| |
| release_sock(sk); |
| return(copied); |
| } |
| |
| |
| int |
| packet_read(struct sock *sk, unsigned char *buff, |
| int len, int noblock, unsigned flags) |
| { |
| return(packet_recvfrom(sk, buff, len, noblock, flags, NULL, NULL)); |
| } |
| |
| |
| struct proto packet_prot = { |
| sock_wmalloc, |
| sock_rmalloc, |
| sock_wfree, |
| sock_rfree, |
| sock_rspace, |
| sock_wspace, |
| packet_close, |
| packet_read, |
| packet_write, |
| packet_sendto, |
| packet_recvfrom, |
| ip_build_header, |
| udp_connect, |
| NULL, |
| ip_queue_xmit, |
| ip_retransmit, |
| NULL, |
| NULL, |
| NULL, |
| udp_select, |
| NULL, |
| packet_init, |
| NULL, |
| 128, |
| 0, |
| {NULL,}, |
| "PACKET" |
| }; |