Add tou_encap structure to inet_sock. In transmit path (ip_queue_xmit) check if encapsulation is enabled and call the build header op if it is. Add IP_TOU_ENCAP setsockopt for IPv4 sockets.
Signed-off-by: Tom Herbert <t...@herbertland.com> --- include/uapi/linux/in.h | 1 + net/ipv4/ip_output.c | 42 ++++++++++++++++++++++++++++++++++++------ net/ipv4/ip_sockglue.c | 7 +++++++ net/ipv4/tou.c | 2 +- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h index eaf9491..9827bff 100644 --- a/include/uapi/linux/in.h +++ b/include/uapi/linux/in.h @@ -152,6 +152,7 @@ struct in_addr { #define MCAST_MSFILTER 48 #define IP_MULTICAST_ALL 49 #define IP_UNICAST_IF 50 +#define IP_TOU_ENCAP 51 #define MCAST_EXCLUDE 0 #define MCAST_INCLUDE 1 diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index cbac493..11cf4de 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -78,6 +78,7 @@ #include <linux/netfilter_bridge.h> #include <linux/netlink.h> #include <linux/tcp.h> +#include <net/ip_tunnels.h> static int ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb, @@ -382,11 +383,38 @@ int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl) struct rtable *rt; struct iphdr *iph; int res; + __be16 dport, sport; + u8 protocol = sk->sk_protocol; + struct ip_tunnel_encap *e; /* Skip all of this if the packet is already routed, * f.e. by something like SCTP. */ rcu_read_lock(); + + e = rcu_dereference(inet->tou_encap); + if (e) { + const struct ip_tunnel_encap_ops *ops; + + /* Transport layer protocol over UDP enapsulation */ + dport = e->dport; + sport = e->sport; + ops = rcu_dereference(iptun_encaps[e->type]); + if (likely(ops && ops->build_header)) { + res = ops->build_header(skb, e, &protocol, + (struct flowi4 *)fl, + sock_net(sk)); + if (res < 0) + goto fail; + } else { + res = -EINVAL; + goto fail; + } + } else { + dport = inet->inet_dport; + sport = inet->inet_sport; + } + inet_opt = rcu_dereference(inet->inet_opt); fl4 = &fl->u.ip4; rt = skb_rtable(skb); @@ -409,9 +437,9 @@ int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl) */ rt = ip_route_output_ports(net, fl4, sk, daddr, inet->inet_saddr, - inet->inet_dport, - inet->inet_sport, - sk->sk_protocol, + dport, + sport, + protocol, RT_CONN_FLAGS(sk), sk->sk_bound_dev_if); if (IS_ERR(rt)) @@ -434,7 +462,7 @@ packet_routed: else iph->frag_off = 0; iph->ttl = ip_select_ttl(inet, &rt->dst); - iph->protocol = sk->sk_protocol; + iph->protocol = protocol; ip_copy_addrs(iph, fl4); /* Transport layer set skb->h.foo itself. */ @@ -456,10 +484,12 @@ packet_routed: return res; no_route: - rcu_read_unlock(); IP_INC_STATS(net, IPSTATS_MIB_OUTNOROUTES); + res = -EHOSTUNREACH; +fail: + rcu_read_unlock(); kfree_skb(skb); - return -EHOSTUNREACH; + return res; } EXPORT_SYMBOL(ip_queue_xmit); diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 71a52f4d..0c9d3f0 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -42,6 +42,7 @@ #include <net/transp_v6.h> #endif #include <net/ip_fib.h> +#include <net/tou.h> #include <linux/errqueue.h> #include <asm/uaccess.h> @@ -1162,6 +1163,10 @@ mc_msf_out: inet->min_ttl = val; break; + case IP_TOU_ENCAP: + err = tou_encap_setsockopt(sk, optval, optlen, false); + break; + default: err = -ENOPROTOOPT; break; @@ -1493,6 +1498,8 @@ static int do_ip_getsockopt(struct sock *sk, int level, int optname, case IP_MINTTL: val = inet->min_ttl; break; + case IPV6_TOU_ENCAP: + return tou_encap_getsockopt(sk, optval, len, optlen, false); default: release_sock(sk); return -ENOPROTOOPT; diff --git a/net/ipv4/tou.c b/net/ipv4/tou.c index ef9999f..d3069a8 100644 --- a/net/ipv4/tou.c +++ b/net/ipv4/tou.c @@ -69,7 +69,7 @@ int tou_encap_setsockopt(struct sock *sk, char __user *optval, int optlen, encap.dport = te.dport; encap.flags = te.flags; - hlen = is_ipv6 ? ip6_encap_hlen(e) : ip_encap_hlen(e); + hlen = is_ipv6 ? ip6_encap_hlen(&encap) : ip_encap_hlen(&encap); if (hlen < 0) return hlen; -- 2.8.0.rc2