packet: add PACKET_RESERVE sockopt
authorPatrick McHardy <kaber@trash.net>
Sat, 19 Jul 2008 01:05:19 +0000 (18:05 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 19 Jul 2008 01:05:19 +0000 (18:05 -0700)
Add new sockopt to reserve some headroom in the mmaped ring frames in
front of the packet payload. This can be used f.i. when the VLAN header
needs to be (re)constructed to avoid moving the entire payload.

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/if_packet.h
net/packet/af_packet.c

index a630295b255f4e35bdab5486e4288914c40a9aee..18db0668065a61c6e5af363f3db50ddd1d5b629a 100644 (file)
@@ -45,6 +45,7 @@ struct sockaddr_ll
 #define PACKET_ORIGDEV                 9
 #define PACKET_VERSION                 10
 #define PACKET_HDRLEN                  11
+#define PACKET_RESERVE                 12
 
 struct tpacket_stats
 {
index db792e02a37feec8b65d9c61976cf69f49c3b140..de73bcb5235ade006a68eee8313804d196f9d7a1 100644 (file)
@@ -188,6 +188,7 @@ struct packet_sock {
        unsigned int            pg_vec_len;
        enum tpacket_versions   tp_version;
        unsigned int            tp_hdrlen;
+       unsigned int            tp_reserve;
 #endif
 };
 
@@ -635,11 +636,13 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
                snaplen = res;
 
        if (sk->sk_type == SOCK_DGRAM) {
-               macoff = netoff = TPACKET_ALIGN(po->tp_hdrlen) + 16;
+               macoff = netoff = TPACKET_ALIGN(po->tp_hdrlen) + 16 +
+                                 po->tp_reserve;
        } else {
                unsigned maclen = skb_network_offset(skb);
                netoff = TPACKET_ALIGN(po->tp_hdrlen +
-                                      (maclen < 16 ? 16 : maclen));
+                                      (maclen < 16 ? 16 : maclen)) +
+                       po->tp_reserve;
                macoff = netoff - maclen;
        }
 
@@ -1448,6 +1451,19 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv
                        return -EINVAL;
                }
        }
+       case PACKET_RESERVE:
+       {
+               unsigned int val;
+
+               if (optlen != sizeof(val))
+                       return -EINVAL;
+               if (po->pg_vec)
+                       return -EBUSY;
+               if (copy_from_user(&val, optval, sizeof(val)))
+                       return -EFAULT;
+               po->tp_reserve = val;
+               return 0;
+       }
 #endif
        case PACKET_AUXDATA:
        {
@@ -1547,6 +1563,12 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
                }
                data = &val;
                break;
+       case PACKET_RESERVE:
+               if (len > sizeof(unsigned int))
+                       len = sizeof(unsigned int);
+               val = po->tp_reserve;
+               data = &val;
+               break;
 #endif
        default:
                return -ENOPROTOOPT;
@@ -1790,7 +1812,8 @@ static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing
                        return -EINVAL;
                if (unlikely(req->tp_block_size & (PAGE_SIZE - 1)))
                        return -EINVAL;
-               if (unlikely(req->tp_frame_size < po->tp_hdrlen))
+               if (unlikely(req->tp_frame_size < po->tp_hdrlen +
+                                                 po->tp_reserve))
                        return -EINVAL;
                if (unlikely(req->tp_frame_size & (TPACKET_ALIGNMENT - 1)))
                        return -EINVAL;