diff options
-rw-r--r-- | include/linux/skbuff.h | 26 | ||||
-rw-r--r-- | include/net/udp.h | 3 | ||||
-rw-r--r-- | net/ipv4/udp_offload.c | 51 | ||||
-rw-r--r-- | net/ipv6/udp_offload.c | 2 |
4 files changed, 73 insertions, 9 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d8f7d74d5a4d..7c5036d11feb 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -596,7 +596,8 @@ struct sk_buff { | |||
596 | __u8 ndisc_nodetype:2; | 596 | __u8 ndisc_nodetype:2; |
597 | #endif | 597 | #endif |
598 | __u8 ipvs_property:1; | 598 | __u8 ipvs_property:1; |
599 | /* 5 or 7 bit hole */ | 599 | __u8 inner_protocol_type:1; |
600 | /* 4 or 6 bit hole */ | ||
600 | 601 | ||
601 | #ifdef CONFIG_NET_SCHED | 602 | #ifdef CONFIG_NET_SCHED |
602 | __u16 tc_index; /* traffic control index */ | 603 | __u16 tc_index; /* traffic control index */ |
@@ -632,7 +633,11 @@ struct sk_buff { | |||
632 | __u32 reserved_tailroom; | 633 | __u32 reserved_tailroom; |
633 | }; | 634 | }; |
634 | 635 | ||
635 | __be16 inner_protocol; | 636 | union { |
637 | __be16 inner_protocol; | ||
638 | __u8 inner_ipproto; | ||
639 | }; | ||
640 | |||
636 | __u16 inner_transport_header; | 641 | __u16 inner_transport_header; |
637 | __u16 inner_network_header; | 642 | __u16 inner_network_header; |
638 | __u16 inner_mac_header; | 643 | __u16 inner_mac_header; |
@@ -1762,6 +1767,23 @@ static inline void skb_reserve(struct sk_buff *skb, int len) | |||
1762 | skb->tail += len; | 1767 | skb->tail += len; |
1763 | } | 1768 | } |
1764 | 1769 | ||
1770 | #define ENCAP_TYPE_ETHER 0 | ||
1771 | #define ENCAP_TYPE_IPPROTO 1 | ||
1772 | |||
1773 | static inline void skb_set_inner_protocol(struct sk_buff *skb, | ||
1774 | __be16 protocol) | ||
1775 | { | ||
1776 | skb->inner_protocol = protocol; | ||
1777 | skb->inner_protocol_type = ENCAP_TYPE_ETHER; | ||
1778 | } | ||
1779 | |||
1780 | static inline void skb_set_inner_ipproto(struct sk_buff *skb, | ||
1781 | __u8 ipproto) | ||
1782 | { | ||
1783 | skb->inner_ipproto = ipproto; | ||
1784 | skb->inner_protocol_type = ENCAP_TYPE_IPPROTO; | ||
1785 | } | ||
1786 | |||
1765 | static inline void skb_reset_inner_headers(struct sk_buff *skb) | 1787 | static inline void skb_reset_inner_headers(struct sk_buff *skb) |
1766 | { | 1788 | { |
1767 | skb->inner_mac_header = skb->mac_header; | 1789 | skb->inner_mac_header = skb->mac_header; |
diff --git a/include/net/udp.h b/include/net/udp.h index 16f4e80f0519..07f9b70962f6 100644 --- a/include/net/udp.h +++ b/include/net/udp.h | |||
@@ -239,7 +239,8 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); | |||
239 | int udp_disconnect(struct sock *sk, int flags); | 239 | int udp_disconnect(struct sock *sk, int flags); |
240 | unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait); | 240 | unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait); |
241 | struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, | 241 | struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, |
242 | netdev_features_t features); | 242 | netdev_features_t features, |
243 | bool is_ipv6); | ||
243 | int udp_lib_getsockopt(struct sock *sk, int level, int optname, | 244 | int udp_lib_getsockopt(struct sock *sk, int level, int optname, |
244 | char __user *optval, int __user *optlen); | 245 | char __user *optval, int __user *optlen); |
245 | int udp_lib_setsockopt(struct sock *sk, int level, int optname, | 246 | int udp_lib_setsockopt(struct sock *sk, int level, int optname, |
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 19ebe6a39ddc..8c35f2c939ee 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c | |||
@@ -25,8 +25,11 @@ struct udp_offload_priv { | |||
25 | struct udp_offload_priv __rcu *next; | 25 | struct udp_offload_priv __rcu *next; |
26 | }; | 26 | }; |
27 | 27 | ||
28 | struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, | 28 | static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, |
29 | netdev_features_t features) | 29 | netdev_features_t features, |
30 | struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb, | ||
31 | netdev_features_t features), | ||
32 | __be16 new_protocol) | ||
30 | { | 33 | { |
31 | struct sk_buff *segs = ERR_PTR(-EINVAL); | 34 | struct sk_buff *segs = ERR_PTR(-EINVAL); |
32 | u16 mac_offset = skb->mac_header; | 35 | u16 mac_offset = skb->mac_header; |
@@ -48,7 +51,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, | |||
48 | skb_reset_mac_header(skb); | 51 | skb_reset_mac_header(skb); |
49 | skb_set_network_header(skb, skb_inner_network_offset(skb)); | 52 | skb_set_network_header(skb, skb_inner_network_offset(skb)); |
50 | skb->mac_len = skb_inner_network_offset(skb); | 53 | skb->mac_len = skb_inner_network_offset(skb); |
51 | skb->protocol = htons(ETH_P_TEB); | 54 | skb->protocol = new_protocol; |
52 | 55 | ||
53 | need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM); | 56 | need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM); |
54 | if (need_csum) | 57 | if (need_csum) |
@@ -56,7 +59,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, | |||
56 | 59 | ||
57 | /* segment inner packet. */ | 60 | /* segment inner packet. */ |
58 | enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); | 61 | enc_features = skb->dev->hw_enc_features & netif_skb_features(skb); |
59 | segs = skb_mac_gso_segment(skb, enc_features); | 62 | segs = gso_inner_segment(skb, enc_features); |
60 | if (IS_ERR_OR_NULL(segs)) { | 63 | if (IS_ERR_OR_NULL(segs)) { |
61 | skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset, | 64 | skb_gso_error_unwind(skb, protocol, tnl_hlen, mac_offset, |
62 | mac_len); | 65 | mac_len); |
@@ -101,6 +104,44 @@ out: | |||
101 | return segs; | 104 | return segs; |
102 | } | 105 | } |
103 | 106 | ||
107 | struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, | ||
108 | netdev_features_t features, | ||
109 | bool is_ipv6) | ||
110 | { | ||
111 | __be16 protocol = skb->protocol; | ||
112 | const struct net_offload **offloads; | ||
113 | const struct net_offload *ops; | ||
114 | struct sk_buff *segs = ERR_PTR(-EINVAL); | ||
115 | struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb, | ||
116 | netdev_features_t features); | ||
117 | |||
118 | rcu_read_lock(); | ||
119 | |||
120 | switch (skb->inner_protocol_type) { | ||
121 | case ENCAP_TYPE_ETHER: | ||
122 | protocol = skb->inner_protocol; | ||
123 | gso_inner_segment = skb_mac_gso_segment; | ||
124 | break; | ||
125 | case ENCAP_TYPE_IPPROTO: | ||
126 | offloads = is_ipv6 ? inet6_offloads : inet_offloads; | ||
127 | ops = rcu_dereference(offloads[skb->inner_ipproto]); | ||
128 | if (!ops || !ops->callbacks.gso_segment) | ||
129 | goto out_unlock; | ||
130 | gso_inner_segment = ops->callbacks.gso_segment; | ||
131 | break; | ||
132 | default: | ||
133 | goto out_unlock; | ||
134 | } | ||
135 | |||
136 | segs = __skb_udp_tunnel_segment(skb, features, gso_inner_segment, | ||
137 | protocol); | ||
138 | |||
139 | out_unlock: | ||
140 | rcu_read_unlock(); | ||
141 | |||
142 | return segs; | ||
143 | } | ||
144 | |||
104 | static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, | 145 | static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, |
105 | netdev_features_t features) | 146 | netdev_features_t features) |
106 | { | 147 | { |
@@ -113,7 +154,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, | |||
113 | if (skb->encapsulation && | 154 | if (skb->encapsulation && |
114 | (skb_shinfo(skb)->gso_type & | 155 | (skb_shinfo(skb)->gso_type & |
115 | (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) { | 156 | (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) { |
116 | segs = skb_udp_tunnel_segment(skb, features); | 157 | segs = skb_udp_tunnel_segment(skb, features, false); |
117 | goto out; | 158 | goto out; |
118 | } | 159 | } |
119 | 160 | ||
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index 212ebfc7973f..8f96988c1db2 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c | |||
@@ -58,7 +58,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, | |||
58 | 58 | ||
59 | if (skb->encapsulation && skb_shinfo(skb)->gso_type & | 59 | if (skb->encapsulation && skb_shinfo(skb)->gso_type & |
60 | (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM)) | 60 | (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM)) |
61 | segs = skb_udp_tunnel_segment(skb, features); | 61 | segs = skb_udp_tunnel_segment(skb, features, true); |
62 | else { | 62 | else { |
63 | const struct ipv6hdr *ipv6h; | 63 | const struct ipv6hdr *ipv6h; |
64 | struct udphdr *uh; | 64 | struct udphdr *uh; |