packet: tx_ring: allow the user to choose tx data offset
The tx data offset of packet mmap tx ring used to be : (TPACKET2_HDRLEN - sizeof(struct sockaddr_ll)) The problem is that, with SOCK_RAW socket, the payload (14 bytes after the beginning of the user data) is misaligned. This patch allows to let the user gives an offset for it's tx data if he desires. Set sock option PACKET_TX_HAS_OFF to 1, then specify in each frame of your tx ring tp_net for SOCK_DGRAM, or tp_mac for SOCK_RAW. Signed-off-by: Paul Chavent <paul.chavent@onera.fr> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
7da716aee2
commit
5920cd3a41
@@ -1881,7 +1881,35 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
|
||||
skb_reserve(skb, hlen);
|
||||
skb_reset_network_header(skb);
|
||||
|
||||
data = ph.raw + po->tp_hdrlen - sizeof(struct sockaddr_ll);
|
||||
if (po->tp_tx_has_off) {
|
||||
int off_min, off_max, off;
|
||||
off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll);
|
||||
off_max = po->tx_ring.frame_size - tp_len;
|
||||
if (sock->type == SOCK_DGRAM) {
|
||||
switch (po->tp_version) {
|
||||
case TPACKET_V2:
|
||||
off = ph.h2->tp_net;
|
||||
break;
|
||||
default:
|
||||
off = ph.h1->tp_net;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (po->tp_version) {
|
||||
case TPACKET_V2:
|
||||
off = ph.h2->tp_mac;
|
||||
break;
|
||||
default:
|
||||
off = ph.h1->tp_mac;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (unlikely((off < off_min) || (off_max < off)))
|
||||
return -EINVAL;
|
||||
data = ph.raw + off;
|
||||
} else {
|
||||
data = ph.raw + po->tp_hdrlen - sizeof(struct sockaddr_ll);
|
||||
}
|
||||
to_write = tp_len;
|
||||
|
||||
if (sock->type == SOCK_DGRAM) {
|
||||
@@ -3109,6 +3137,19 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv
|
||||
|
||||
return fanout_add(sk, val & 0xffff, val >> 16);
|
||||
}
|
||||
case PACKET_TX_HAS_OFF:
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
if (optlen != sizeof(val))
|
||||
return -EINVAL;
|
||||
if (po->rx_ring.pg_vec || po->tx_ring.pg_vec)
|
||||
return -EBUSY;
|
||||
if (copy_from_user(&val, optval, sizeof(val)))
|
||||
return -EFAULT;
|
||||
po->tp_tx_has_off = !!val;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
@@ -3200,6 +3241,9 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
|
||||
((u32)po->fanout->type << 16)) :
|
||||
0);
|
||||
break;
|
||||
case PACKET_TX_HAS_OFF:
|
||||
val = po->tp_tx_has_off;
|
||||
break;
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
}
|
||||
|
Reference in New Issue
Block a user