aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/skbuff.h26
-rw-r--r--include/net/udp.h3
-rw-r--r--net/ipv4/udp_offload.c51
-rw-r--r--net/ipv6/udp_offload.c2
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
1773static 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
1780static 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
1765static inline void skb_reset_inner_headers(struct sk_buff *skb) 1787static 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);
239int udp_disconnect(struct sock *sk, int flags); 239int udp_disconnect(struct sock *sk, int flags);
240unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait); 240unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait);
241struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, 241struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
242 netdev_features_t features); 242 netdev_features_t features,
243 bool is_ipv6);
243int udp_lib_getsockopt(struct sock *sk, int level, int optname, 244int udp_lib_getsockopt(struct sock *sk, int level, int optname,
244 char __user *optval, int __user *optlen); 245 char __user *optval, int __user *optlen);
245int udp_lib_setsockopt(struct sock *sk, int level, int optname, 246int 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
28struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, 28static 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
107struct 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
139out_unlock:
140 rcu_read_unlock();
141
142 return segs;
143}
144
104static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, 145static 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;