diff options
Diffstat (limited to 'net/packet/af_packet.c')
-rw-r--r-- | net/packet/af_packet.c | 46 |
1 files changed, 45 insertions, 1 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 9034f52659b5..f262dbfc7f06 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c | |||
@@ -1881,7 +1881,35 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, | |||
1881 | skb_reserve(skb, hlen); | 1881 | skb_reserve(skb, hlen); |
1882 | skb_reset_network_header(skb); | 1882 | skb_reset_network_header(skb); |
1883 | 1883 | ||
1884 | data = ph.raw + po->tp_hdrlen - sizeof(struct sockaddr_ll); | 1884 | if (po->tp_tx_has_off) { |
1885 | int off_min, off_max, off; | ||
1886 | off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll); | ||
1887 | off_max = po->tx_ring.frame_size - tp_len; | ||
1888 | if (sock->type == SOCK_DGRAM) { | ||
1889 | switch (po->tp_version) { | ||
1890 | case TPACKET_V2: | ||
1891 | off = ph.h2->tp_net; | ||
1892 | break; | ||
1893 | default: | ||
1894 | off = ph.h1->tp_net; | ||
1895 | break; | ||
1896 | } | ||
1897 | } else { | ||
1898 | switch (po->tp_version) { | ||
1899 | case TPACKET_V2: | ||
1900 | off = ph.h2->tp_mac; | ||
1901 | break; | ||
1902 | default: | ||
1903 | off = ph.h1->tp_mac; | ||
1904 | break; | ||
1905 | } | ||
1906 | } | ||
1907 | if (unlikely((off < off_min) || (off_max < off))) | ||
1908 | return -EINVAL; | ||
1909 | data = ph.raw + off; | ||
1910 | } else { | ||
1911 | data = ph.raw + po->tp_hdrlen - sizeof(struct sockaddr_ll); | ||
1912 | } | ||
1885 | to_write = tp_len; | 1913 | to_write = tp_len; |
1886 | 1914 | ||
1887 | if (sock->type == SOCK_DGRAM) { | 1915 | if (sock->type == SOCK_DGRAM) { |
@@ -3109,6 +3137,19 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv | |||
3109 | 3137 | ||
3110 | return fanout_add(sk, val & 0xffff, val >> 16); | 3138 | return fanout_add(sk, val & 0xffff, val >> 16); |
3111 | } | 3139 | } |
3140 | case PACKET_TX_HAS_OFF: | ||
3141 | { | ||
3142 | unsigned int val; | ||
3143 | |||
3144 | if (optlen != sizeof(val)) | ||
3145 | return -EINVAL; | ||
3146 | if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) | ||
3147 | return -EBUSY; | ||
3148 | if (copy_from_user(&val, optval, sizeof(val))) | ||
3149 | return -EFAULT; | ||
3150 | po->tp_tx_has_off = !!val; | ||
3151 | return 0; | ||
3152 | } | ||
3112 | default: | 3153 | default: |
3113 | return -ENOPROTOOPT; | 3154 | return -ENOPROTOOPT; |
3114 | } | 3155 | } |
@@ -3200,6 +3241,9 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, | |||
3200 | ((u32)po->fanout->type << 16)) : | 3241 | ((u32)po->fanout->type << 16)) : |
3201 | 0); | 3242 | 0); |
3202 | break; | 3243 | break; |
3244 | case PACKET_TX_HAS_OFF: | ||
3245 | val = po->tp_tx_has_off; | ||
3246 | break; | ||
3203 | default: | 3247 | default: |
3204 | return -ENOPROTOOPT; | 3248 | return -ENOPROTOOPT; |
3205 | } | 3249 | } |