summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/if_packet.h10
-rw-r--r--net/packet/af_packet.c57
2 files changed, 58 insertions, 9 deletions
diff --git a/include/linux/if_packet.h b/include/linux/if_packet.h
index 99393ef3af39..f3de05c30678 100644
--- a/include/linux/if_packet.h
+++ b/include/linux/if_packet.h
@@ -41,6 +41,7 @@ struct sockaddr_ll
#define PACKET_RX_RING 5
#define PACKET_STATISTICS 6
#define PACKET_COPY_THRESH 7
+#define PACKET_AUXDATA 8
struct tpacket_stats
{
@@ -48,6 +49,15 @@ struct tpacket_stats
unsigned int tp_drops;
};
+struct tpacket_auxdata
+{
+ __u32 tp_status;
+ __u32 tp_len;
+ __u32 tp_snaplen;
+ __u16 tp_mac;
+ __u16 tp_net;
+};
+
struct tpacket_hdr
{
unsigned long tp_status;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 6dc01bdeb76b..8973ea78831e 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -200,7 +200,8 @@ struct packet_sock {
#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
@@ -214,6 +215,8 @@ struct packet_sock {
#endif
};
+#define PACKET_SKB_CB(__skb) ((struct tpacket_auxdata *)((__skb)->cb))
+
#ifdef CONFIG_PACKET_MMAP
static inline char *packet_lookup_frame(struct packet_sock *po, unsigned int position)
@@ -462,6 +465,7 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
u8 * skb_head = skb->data;
int skb_len = skb->len;
unsigned int snaplen, res;
+ struct tpacket_auxdata *aux;
if (skb->pkt_type == PACKET_LOOPBACK)
goto drop;
@@ -523,6 +527,15 @@ static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
if (dev->hard_header_parse)
sll->sll_halen = dev->hard_header_parse(skb, sll->sll_addr);
+ aux = PACKET_SKB_CB(skb);
+ aux->tp_status = TP_STATUS_USER;
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ aux->tp_status |= TP_STATUS_CSUMNOTREADY;
+ aux->tp_len = skb->len;
+ aux->tp_snaplen = snaplen;
+ aux->tp_mac = 0;
+ aux->tp_net = skb->nh.raw - skb->data;
+
if (pskb_trim(skb, snaplen))
goto drop_n_acct;
@@ -582,11 +595,12 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
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);
@@ -1119,6 +1133,11 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
if (msg->msg_name)
memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+ if (pkt_sk(sk)->auxdata) {
+ struct tpacket_auxdata *aux = PACKET_SKB_CB(skb);
+ put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(*aux), aux);
+ }
+
/*
* Free or return the buffer as appropriate. Again this
* hides all the races and re-entrancy issues from us.
@@ -1317,6 +1336,7 @@ static int
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)
@@ -1369,6 +1389,18 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv
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;
}
@@ -1378,8 +1410,11 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
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;
@@ -1392,9 +1427,6 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
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);
@@ -1403,16 +1435,23 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
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;
}
OpenPOWER on IntegriCloud