* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Alan Cox, <gw4pts@gw4pts.ampr.org>
*
- * Fixes:
+ * Fixes:
* Alan Cox : verify_area() now used correctly
* Alan Cox : new skbuff lists, look ma no backlogs!
* Alan Cox : tidied skbuff lists.
* Alexey Kuznetsov : Untied from IPv4 stack.
* Cyrus Durgin : Fixed kerneld for kmod.
* Michal Ostrowski : Module initialization cleanup.
- * Ulises Alonso : Frame number limit removal and
+ * Ulises Alonso : Frame number limit removal and
* packet_set_ring memory leak.
* Eric Biederman : Allow for > 8 byte hardware addresses.
* The convention is that longer addresses
* will simply extend the hardware address
- * byte arrays at the end of sockaddr_ll
+ * byte arrays at the end of sockaddr_ll
* and packet_mreq.
*
* This program is free software; you can redistribute it and/or
* 2 of the License, or (at your option) any later version.
*
*/
-
+
#include <linux/types.h>
-#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/capability.h>
#include <linux/fcntl.h>
#include <linux/netdevice.h>
#include <linux/if_packet.h>
#include <linux/wireless.h>
+#include <linux/kernel.h>
#include <linux/kmod.h>
#include <net/ip.h>
#include <net/protocol.h>
Incoming, dev->hard_header==NULL
mac.raw -> UNKNOWN position. It is very likely, that it points to ll header.
- PPP makes it, that is wrong, because introduce assymetry
+ PPP makes it, that is wrong, because introduce assymetry
between rx and tx paths.
data -> data
#endif
struct packet_type prot_hook;
spinlock_t bind_lock;
- char running; /* prot_hook is attached*/
+ unsigned int running:1, /* prot_hook is attached*/
+ auxdata:1;
int ifindex; /* bound device */
__be16 num;
#ifdef CONFIG_PACKET_MULTICAST
#endif
};
+struct packet_skb_cb {
+ unsigned int origlen;
+ union {
+ struct sockaddr_pkt pkt;
+ struct sockaddr_ll ll;
+ } sa;
+};
+
+#define PACKET_SKB_CB(__skb) ((struct packet_skb_cb *)((__skb)->cb))
+
#ifdef CONFIG_PACKET_MMAP
-static inline char *packet_lookup_frame(struct packet_sock *po, unsigned int position)
+static inline struct tpacket_hdr *packet_lookup_frame(struct packet_sock *po, unsigned int position)
{
unsigned int pg_vec_pos, frame_offset;
- char *frame;
pg_vec_pos = position / po->frames_per_block;
frame_offset = position % po->frames_per_block;
- frame = po->pg_vec[pg_vec_pos] + (frame_offset * po->frame_size);
-
- return frame;
+ return (struct tpacket_hdr *)(po->pg_vec[pg_vec_pos] + (frame_offset * po->frame_size));
}
#endif
*/
sk = pt->af_packet_priv;
-
+
/*
* Yank back the headers [hope the device set this
* right or kerboom...]
/* drop conntrack reference */
nf_reset(skb);
- spkt = (struct sockaddr_pkt*)skb->cb;
+ spkt = &PACKET_SKB_CB(skb)->sa.pkt;
skb_push(skb, skb->data-skb->mac.raw);
* Output a raw packet to a device layer. This bypasses all the other
* protocol layers and you must therefore supply it with a complete frame
*/
-
+
static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len)
{
struct net_device *dev;
__be16 proto=0;
int err;
-
+
/*
- * Get and verify the address.
+ * Get and verify the address.
*/
if (saddr)
return(-ENOTCONN); /* SOCK_PACKET must be sent giving an address */
/*
- * Find the device first to size check it
+ * Find the device first to size check it
*/
saddr->spkt_device[13] = 0;
err = -ENODEV;
if (dev == NULL)
goto out_unlock;
-
+
err = -ENETDOWN;
if (!(dev->flags & IFF_UP))
goto out_unlock;
* You may not queue a frame bigger than the mtu. This is the lowest level
* raw protocol and you must do your own fragmentation at this level.
*/
-
+
err = -EMSGSIZE;
if (len > dev->mtu + dev->hard_header_len)
goto out_unlock;
* deal with the problem - do your own algorithmic backoffs. That's far
* more flexible.
*/
-
- if (skb == NULL)
+
+ if (skb == NULL)
goto out_unlock;
/*
- * Fill it in
+ * Fill it in
*/
-
+
/* FIXME: Save some space for broken drivers that write a
* hard header at transmission time by themselves. PPP is the
* notable one here. This should really be fixed at the driver level.
skb = nskb;
}
- sll = (struct sockaddr_ll*)skb->cb;
+ BUILD_BUG_ON(sizeof(*PACKET_SKB_CB(skb)) + MAX_ADDR_LEN - 8 >
+ sizeof(skb->cb));
+
+ sll = &PACKET_SKB_CB(skb)->sa.ll;
sll->sll_family = AF_PACKET;
sll->sll_hatype = dev->type;
sll->sll_protocol = skb->protocol;
if (dev->hard_header_parse)
sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr);
+ PACKET_SKB_CB(skb)->origlen = skb->len;
+
if (pskb_trim(skb, snaplen))
goto drop_n_acct;
else if (skb->pkt_type == PACKET_OUTGOING) {
/* Special case: outgoing packets have ll header at head */
skb_pull(skb, skb->nh.raw - skb->data);
- if (skb->ip_summed == CHECKSUM_PARTIAL)
- status |= TP_STATUS_CSUMNOTREADY;
}
}
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ status |= TP_STATUS_CSUMNOTREADY;
+
snaplen = skb->len;
res = run_filter(skb, sk, snaplen);
}
spin_lock(&sk->sk_receive_queue.lock);
- h = (struct tpacket_hdr *)packet_lookup_frame(po, po->head);
-
+ h = packet_lookup_frame(po, po->head);
+
if (h->tp_status)
goto ring_is_full;
po->head = po->head != po->frame_max ? po->head+1 : 0;
h->tp_snaplen = snaplen;
h->tp_mac = macoff;
h->tp_net = netoff;
- if (skb->tstamp.off_sec == 0) {
+ if (skb->tstamp.off_sec == 0) {
__net_timestamp(skb);
sock_enable_timestamp(sk);
}
skb->len = skb_len;
}
drop:
- kfree_skb(skb);
+ kfree_skb(skb);
return 0;
ring_is_full:
int ifindex, err, reserve = 0;
/*
- * Get and verify the address.
+ * Get and verify the address.
*/
-
+
if (saddr == NULL) {
struct packet_sock *po = pkt_sk(sk);
char name[15];
struct net_device *dev;
int err = -ENODEV;
-
+
/*
* Check legality
*/
-
+
if (addr_len != sizeof(struct sockaddr))
return -EINVAL;
strlcpy(name,uaddr->sa_data,sizeof(name));
/*
* Check legality
*/
-
+
if (addr_len < sizeof(struct sockaddr_ll))
return -EINVAL;
if (sll->sll_family != AF_PACKET)
};
/*
- * Create a packet of type SOCK_PACKET.
+ * Create a packet of type SOCK_PACKET.
*/
static int packet_create(struct socket *sock, int protocol)
skb=skb_recv_datagram(sk,flags,flags&MSG_DONTWAIT,&err);
/*
- * An error occurred so return it. Because skb_recv_datagram()
+ * An error occurred so return it. Because skb_recv_datagram()
* handles the blocking we don't see and worry about blocking
* retries.
*/
* it in now.
*/
- sll = (struct sockaddr_ll*)skb->cb;
+ sll = &PACKET_SKB_CB(skb)->sa.ll;
if (sock->type == SOCK_PACKET)
msg->msg_namelen = sizeof(struct sockaddr_pkt);
else
sock_recv_timestamp(msg, sk, skb);
if (msg->msg_name)
- memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+ memcpy(msg->msg_name, &PACKET_SKB_CB(skb)->sa,
+ msg->msg_namelen);
+
+ if (pkt_sk(sk)->auxdata) {
+ struct tpacket_auxdata aux;
+
+ aux.tp_status = TP_STATUS_USER;
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ aux.tp_status |= TP_STATUS_CSUMNOTREADY;
+ aux.tp_len = PACKET_SKB_CB(skb)->origlen;
+ aux.tp_snaplen = skb->len;
+ aux.tp_mac = 0;
+ aux.tp_net = skb->nh.raw - skb->data;
+
+ put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux);
+ }
/*
* Free or return the buffer as appropriate. Again this
packet_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
{
struct sock *sk = sock->sk;
+ struct packet_sock *po = pkt_sk(sk);
int ret;
if (level != SOL_PACKET)
switch(optname) {
#ifdef CONFIG_PACKET_MULTICAST
- case PACKET_ADD_MEMBERSHIP:
+ case PACKET_ADD_MEMBERSHIP:
case PACKET_DROP_MEMBERSHIP:
{
struct packet_mreq_max mreq;
return 0;
}
#endif
+ case PACKET_AUXDATA:
+ {
+ int val;
+
+ if (optlen < sizeof(val))
+ return -EINVAL;
+ if (copy_from_user(&val, optval, sizeof(val)))
+ return -EFAULT;
+
+ po->auxdata = !!val;
+ return 0;
+ }
default:
return -ENOPROTOOPT;
}
char __user *optval, int __user *optlen)
{
int len;
+ int val;
struct sock *sk = sock->sk;
struct packet_sock *po = pkt_sk(sk);
+ void *data;
+ struct tpacket_stats st;
if (level != SOL_PACKET)
return -ENOPROTOOPT;
if (len < 0)
return -EINVAL;
-
+
switch(optname) {
case PACKET_STATISTICS:
- {
- struct tpacket_stats st;
-
if (len > sizeof(struct tpacket_stats))
len = sizeof(struct tpacket_stats);
spin_lock_bh(&sk->sk_receive_queue.lock);
spin_unlock_bh(&sk->sk_receive_queue.lock);
st.tp_packets += st.tp_drops;
- if (copy_to_user(optval, &st, len))
- return -EFAULT;
+ data = &st;
+ break;
+ case PACKET_AUXDATA:
+ if (len > sizeof(int))
+ len = sizeof(int);
+ val = po->auxdata;
+
+ data = &val;
break;
- }
default:
return -ENOPROTOOPT;
}
if (put_user(len, optlen))
return -EFAULT;
+ if (copy_to_user(optval, data, len))
+ return -EFAULT;
return 0;
}
{
struct sock *sk;
struct hlist_node *node;
- struct net_device *dev = (struct net_device*)data;
+ struct net_device *dev = data;
read_lock(&packet_sklist_lock);
sk_for_each(sk, node, &packet_sklist) {
}
case SIOCGSTAMP:
return sock_get_timestamp(sk, (struct timeval __user *)arg);
-
+
#ifdef CONFIG_INET
case SIOCADDRT:
case SIOCDELRT:
unsigned last = po->head ? po->head-1 : po->frame_max;
struct tpacket_hdr *h;
- h = (struct tpacket_hdr *)packet_lookup_frame(po, last);
+ h = packet_lookup_frame(po, last);
if (h->tp_status)
mask |= POLLIN | POLLRDNORM;
struct file *file = vma->vm_file;
struct socket * sock = file->private_data;
struct sock *sk = sock->sk;
-
+
if (sk)
atomic_inc(&pkt_sk(sk)->mapped);
}
struct file *file = vma->vm_file;
struct socket * sock = file->private_data;
struct sock *sk = sock->sk;
-
+
if (sk)
atomic_dec(&pkt_sk(sk)->mapped);
}
int was_running, order = 0;
__be16 num;
int err = 0;
-
+
if (req->tp_block_nr) {
int i, l;
__sock_put(sk);
}
spin_unlock(&po->bind_lock);
-
+
synchronize_net();
err = -EBUSY;
.connect = sock_no_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
- .getname = packet_getname,
+ .getname = packet_getname,
.poll = packet_poll,
.ioctl = packet_ioctl,
.listen = sock_no_listen,
static void *packet_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
++*pos;
- return (v == SEQ_START_TOKEN)
- ? sk_head(&packet_sklist)
+ return (v == SEQ_START_TOKEN)
+ ? sk_head(&packet_sklist)
: sk_next((struct sock*)v) ;
}
static void packet_seq_stop(struct seq_file *seq, void *v)
{
- read_unlock(&packet_sklist_lock);
+ read_unlock(&packet_sklist_lock);
}
-static int packet_seq_show(struct seq_file *seq, void *v)
+static int packet_seq_show(struct seq_file *seq, void *v)
{
if (v == SEQ_START_TOKEN)
seq_puts(seq, "sk RefCnt Type Proto Iface R Rmem User Inode\n");
return seq_open(file, &packet_seq_ops);
}
-static struct file_operations packet_seq_fops = {
+static const struct file_operations packet_seq_fops = {
.owner = THIS_MODULE,
.open = packet_seq_open,
.read = seq_read,