123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <linux/module.h>
223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <linux/errno.h>
323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <linux/socket.h>
423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <linux/skbuff.h>
523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <linux/ip.h>
623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <linux/udp.h>
723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <linux/types.h>
823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <linux/kernel.h>
923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <net/genetlink.h>
1037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert#include <net/gue.h>
1123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <net/ip.h>
12afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert#include <net/protocol.h>
1323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <net/udp.h>
1423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <net/udp_tunnel.h>
1523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <net/xfrm.h>
1623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <uapi/linux/fou.h>
1723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert#include <uapi/linux/genetlink.h>
1823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
1923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic DEFINE_SPINLOCK(fou_lock);
2023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic LIST_HEAD(fou_list);
2123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
2223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstruct fou {
2323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct socket *sock;
2423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	u8 protocol;
2523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	u16 port;
26afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	struct udp_offload udp_offloads;
2723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct list_head list;
2823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert};
2923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
3023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstruct fou_cfg {
3137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	u16 type;
3223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	u8 protocol;
3323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct udp_port_cfg udp_config;
3423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert};
3523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
3623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic inline struct fou *fou_from_sock(struct sock *sk)
3723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
3823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	return sk->sk_user_data;
3923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
4023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
4123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic int fou_udp_encap_recv_deliver(struct sk_buff *skb,
4223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert				      u8 protocol, size_t len)
4323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
4423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct iphdr *iph = ip_hdr(skb);
4523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
4623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	/* Remove 'len' bytes from the packet (UDP header and
4723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	 * FOU header if present), modify the protocol to the one
4823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	 * we found, and then call rcv_encap.
4923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	 */
5023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	iph->tot_len = htons(ntohs(iph->tot_len) - len);
5123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	__skb_pull(skb, len);
5223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	skb_postpull_rcsum(skb, udp_hdr(skb), len);
5323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	skb_reset_transport_header(skb);
5423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
5523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	return -protocol;
5623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
5723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
5823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
5923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
6023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct fou *fou = fou_from_sock(sk);
6123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
6223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	if (!fou)
6323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		return 1;
6423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
6523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	return fou_udp_encap_recv_deliver(skb, fou->protocol,
6623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert					  sizeof(struct udphdr));
6723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
6823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
6937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbertstatic int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
7037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert{
7137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	struct fou *fou = fou_from_sock(sk);
7237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	size_t len;
7337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	struct guehdr *guehdr;
7437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	struct udphdr *uh;
7537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
7637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	if (!fou)
7737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		return 1;
7837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
7937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	len = sizeof(struct udphdr) + sizeof(struct guehdr);
8037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	if (!pskb_may_pull(skb, len))
8137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		goto drop;
8237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
8337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	uh = udp_hdr(skb);
8437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	guehdr = (struct guehdr *)&uh[1];
8537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
8637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	len += guehdr->hlen << 2;
8737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	if (!pskb_may_pull(skb, len))
8837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		goto drop;
8937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
90d8f00d27105a1553a13d4a96c3eb4544f70ca908Li RongQing	uh = udp_hdr(skb);
91d8f00d27105a1553a13d4a96c3eb4544f70ca908Li RongQing	guehdr = (struct guehdr *)&uh[1];
92d8f00d27105a1553a13d4a96c3eb4544f70ca908Li RongQing
9337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	if (guehdr->version != 0)
9437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		goto drop;
9537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
9637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	if (guehdr->flags) {
9737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		/* No support yet */
9837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		goto drop;
9937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	}
10037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
10137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	return fou_udp_encap_recv_deliver(skb, guehdr->next_hdr, len);
10237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbertdrop:
10337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	kfree_skb(skb);
10437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	return 0;
10537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert}
10637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
107afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbertstatic struct sk_buff **fou_gro_receive(struct sk_buff **head,
108efc98d08e1ec4fd131f794370b274dceaf32c958Tom Herbert					struct sk_buff *skb)
109afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert{
110afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	const struct net_offload *ops;
111afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	struct sk_buff **pp = NULL;
112afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	u8 proto = NAPI_GRO_CB(skb)->proto;
113efc98d08e1ec4fd131f794370b274dceaf32c958Tom Herbert	const struct net_offload **offloads;
114afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert
115afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	rcu_read_lock();
116efc98d08e1ec4fd131f794370b274dceaf32c958Tom Herbert	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
117afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	ops = rcu_dereference(offloads[proto]);
118afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	if (!ops || !ops->callbacks.gro_receive)
119afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert		goto out_unlock;
120afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert
121afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	pp = ops->callbacks.gro_receive(head, skb);
122afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert
123afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbertout_unlock:
124afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	rcu_read_unlock();
125afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert
126afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	return pp;
127afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert}
128afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert
129efc98d08e1ec4fd131f794370b274dceaf32c958Tom Herbertstatic int fou_gro_complete(struct sk_buff *skb, int nhoff)
130afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert{
131afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	const struct net_offload *ops;
132afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	u8 proto = NAPI_GRO_CB(skb)->proto;
133afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	int err = -ENOSYS;
134efc98d08e1ec4fd131f794370b274dceaf32c958Tom Herbert	const struct net_offload **offloads;
135afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert
136cfdf1e1ba5bf55e095cf4bcaa9585c4759f239e8Jesse Gross	udp_tunnel_gro_complete(skb, nhoff);
137cfdf1e1ba5bf55e095cf4bcaa9585c4759f239e8Jesse Gross
138afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	rcu_read_lock();
139efc98d08e1ec4fd131f794370b274dceaf32c958Tom Herbert	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
140afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	ops = rcu_dereference(offloads[proto]);
141afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	if (WARN_ON(!ops || !ops->callbacks.gro_complete))
142afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert		goto out_unlock;
143afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert
144afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	err = ops->callbacks.gro_complete(skb, nhoff);
145afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert
146afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbertout_unlock:
147afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	rcu_read_unlock();
148afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert
149afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	return err;
150afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert}
151afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert
15237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbertstatic struct sk_buff **gue_gro_receive(struct sk_buff **head,
15337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert					struct sk_buff *skb)
15437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert{
15537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	const struct net_offload **offloads;
15637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	const struct net_offload *ops;
15737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	struct sk_buff **pp = NULL;
15837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	struct sk_buff *p;
15937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	u8 proto;
16037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	struct guehdr *guehdr;
16137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	unsigned int hlen, guehlen;
16237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	unsigned int off;
16337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	int flush = 1;
16437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
16537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	off = skb_gro_offset(skb);
16637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	hlen = off + sizeof(*guehdr);
16737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	guehdr = skb_gro_header_fast(skb, off);
16837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	if (skb_gro_header_hard(skb, hlen)) {
16937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		guehdr = skb_gro_header_slow(skb, hlen, off);
17037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		if (unlikely(!guehdr))
17137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert			goto out;
17237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	}
17337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
17437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	proto = guehdr->next_hdr;
17537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
17637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	rcu_read_lock();
17737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
17837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	ops = rcu_dereference(offloads[proto]);
17937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	if (WARN_ON(!ops || !ops->callbacks.gro_receive))
18037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		goto out_unlock;
18137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
18237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
18337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
18437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	hlen = off + guehlen;
18537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	if (skb_gro_header_hard(skb, hlen)) {
18637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		guehdr = skb_gro_header_slow(skb, hlen, off);
18737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		if (unlikely(!guehdr))
18837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert			goto out_unlock;
18937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	}
19037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
19137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	flush = 0;
19237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
19337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	for (p = *head; p; p = p->next) {
19437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		const struct guehdr *guehdr2;
19537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
19637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		if (!NAPI_GRO_CB(p)->same_flow)
19737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert			continue;
19837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
19937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		guehdr2 = (struct guehdr *)(p->data + off);
20037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
20137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		/* Compare base GUE header to be equal (covers
20237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		 * hlen, version, next_hdr, and flags.
20337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		 */
20437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		if (guehdr->word != guehdr2->word) {
20537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert			NAPI_GRO_CB(p)->same_flow = 0;
20637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert			continue;
20737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		}
20837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
20937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		/* Compare optional fields are the same. */
21037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		if (guehdr->hlen && memcmp(&guehdr[1], &guehdr2[1],
21137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert					   guehdr->hlen << 2)) {
21237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert			NAPI_GRO_CB(p)->same_flow = 0;
21337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert			continue;
21437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		}
21537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	}
21637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
21737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	skb_gro_pull(skb, guehlen);
21837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
21937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	/* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
22037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	skb_gro_postpull_rcsum(skb, guehdr, guehlen);
22137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
22237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	pp = ops->callbacks.gro_receive(head, skb);
22337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
22437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbertout_unlock:
22537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	rcu_read_unlock();
22637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbertout:
22737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	NAPI_GRO_CB(skb)->flush |= flush;
22837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
22937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	return pp;
23037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert}
23137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
23237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbertstatic int gue_gro_complete(struct sk_buff *skb, int nhoff)
23337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert{
23437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	const struct net_offload **offloads;
23537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	struct guehdr *guehdr = (struct guehdr *)(skb->data + nhoff);
23637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	const struct net_offload *ops;
23737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	unsigned int guehlen;
23837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	u8 proto;
23937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	int err = -ENOENT;
24037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
24137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	proto = guehdr->next_hdr;
24237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
24337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
24437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
24537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	rcu_read_lock();
24637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
24737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	ops = rcu_dereference(offloads[proto]);
24837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	if (WARN_ON(!ops || !ops->callbacks.gro_complete))
24937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		goto out_unlock;
25037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
25137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	err = ops->callbacks.gro_complete(skb, nhoff + guehlen);
25237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
25337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbertout_unlock:
25437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	rcu_read_unlock();
25537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	return err;
25637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert}
25737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
25823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic int fou_add_to_port_list(struct fou *fou)
25923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
26023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct fou *fout;
26123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
26223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	spin_lock(&fou_lock);
26323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	list_for_each_entry(fout, &fou_list, list) {
26423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		if (fou->port == fout->port) {
26523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert			spin_unlock(&fou_lock);
26623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert			return -EALREADY;
26723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		}
26823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	}
26923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
27023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	list_add(&fou->list, &fou_list);
27123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	spin_unlock(&fou_lock);
27223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
27323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	return 0;
27423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
27523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
27623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic void fou_release(struct fou *fou)
27723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
27823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct socket *sock = fou->sock;
27923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct sock *sk = sock->sk;
28023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
28123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	udp_del_offload(&fou->udp_offloads);
28223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
28323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	list_del(&fou->list);
28423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
28523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	/* Remove hooks into tunnel socket */
28623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	sk->sk_user_data = NULL;
28723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
28823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	sock_release(sock);
28923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
29023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	kfree(fou);
29123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
29223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
29337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbertstatic int fou_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg)
29437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert{
29537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	udp_sk(sk)->encap_rcv = fou_udp_recv;
29637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	fou->protocol = cfg->protocol;
29737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	fou->udp_offloads.callbacks.gro_receive = fou_gro_receive;
29837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	fou->udp_offloads.callbacks.gro_complete = fou_gro_complete;
29937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	fou->udp_offloads.port = cfg->udp_config.local_udp_port;
30037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	fou->udp_offloads.ipproto = cfg->protocol;
30137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
30237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	return 0;
30337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert}
30437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
30537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbertstatic int gue_encap_init(struct sock *sk, struct fou *fou, struct fou_cfg *cfg)
30637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert{
30737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	udp_sk(sk)->encap_rcv = gue_udp_recv;
30837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	fou->udp_offloads.callbacks.gro_receive = gue_gro_receive;
30937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	fou->udp_offloads.callbacks.gro_complete = gue_gro_complete;
31037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	fou->udp_offloads.port = cfg->udp_config.local_udp_port;
31137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
31237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	return 0;
31337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert}
31437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
31523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic int fou_create(struct net *net, struct fou_cfg *cfg,
31623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		      struct socket **sockp)
31723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
31823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct fou *fou = NULL;
31923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	int err;
32023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct socket *sock = NULL;
32123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct sock *sk;
32223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
32323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	/* Open UDP socket */
32423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	err = udp_sock_create(net, &cfg->udp_config, &sock);
32523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	if (err < 0)
32623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		goto error;
32723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
32823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	/* Allocate FOU port structure */
32923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	fou = kzalloc(sizeof(*fou), GFP_KERNEL);
33023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	if (!fou) {
33123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		err = -ENOMEM;
33223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		goto error;
33323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	}
33423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
33523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	sk = sock->sk;
33623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
33737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	fou->port = cfg->udp_config.local_udp_port;
33837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
33937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	/* Initial for fou type */
34037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	switch (cfg->type) {
34137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	case FOU_ENCAP_DIRECT:
34237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		err = fou_encap_init(sk, fou, cfg);
34337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		if (err)
34437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert			goto error;
34537dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		break;
34637dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	case FOU_ENCAP_GUE:
34737dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		err = gue_encap_init(sk, fou, cfg);
34837dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		if (err)
34937dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert			goto error;
35037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		break;
35137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	default:
35237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		err = -EINVAL;
35337dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		goto error;
35437dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	}
35523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
35623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	udp_sk(sk)->encap_type = 1;
35723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	udp_encap_enable();
35823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
35923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	sk->sk_user_data = fou;
36023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	fou->sock = sock;
36123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
36223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	udp_set_convert_csum(sk, true);
36323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
36423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	sk->sk_allocation = GFP_ATOMIC;
36523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
366afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	if (cfg->udp_config.family == AF_INET) {
367afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert		err = udp_add_offload(&fou->udp_offloads);
368afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert		if (err)
369afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert			goto error;
370afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert	}
371afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert
37223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	err = fou_add_to_port_list(fou);
37323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	if (err)
37423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		goto error;
37523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
37623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	if (sockp)
37723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		*sockp = sock;
37823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
37923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	return 0;
38023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
38123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herberterror:
38223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	kfree(fou);
38323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	if (sock)
38423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		sock_release(sock);
38523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
38623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	return err;
38723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
38823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
38923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic int fou_destroy(struct net *net, struct fou_cfg *cfg)
39023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
39123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct fou *fou;
39223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	u16 port = cfg->udp_config.local_udp_port;
39323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	int err = -EINVAL;
39423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
39523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	spin_lock(&fou_lock);
39623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	list_for_each_entry(fou, &fou_list, list) {
39723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		if (fou->port == port) {
398afe93325bc02a5b2dea0cd7d78225de692265e6eTom Herbert			udp_del_offload(&fou->udp_offloads);
39923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert			fou_release(fou);
40023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert			err = 0;
40123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert			break;
40223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		}
40323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	}
40423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	spin_unlock(&fou_lock);
40523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
40623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	return err;
40723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
40823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
40923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic struct genl_family fou_nl_family = {
41023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	.id		= GENL_ID_GENERATE,
41123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	.hdrsize	= 0,
41223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	.name		= FOU_GENL_NAME,
41323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	.version	= FOU_GENL_VERSION,
41423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	.maxattr	= FOU_ATTR_MAX,
41523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	.netnsok	= true,
41623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert};
41723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
41823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic struct nla_policy fou_nl_policy[FOU_ATTR_MAX + 1] = {
41923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	[FOU_ATTR_PORT] = { .type = NLA_U16, },
42023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	[FOU_ATTR_AF] = { .type = NLA_U8, },
42123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	[FOU_ATTR_IPPROTO] = { .type = NLA_U8, },
42237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	[FOU_ATTR_TYPE] = { .type = NLA_U8, },
42323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert};
42423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
42523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic int parse_nl_config(struct genl_info *info,
42623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert			   struct fou_cfg *cfg)
42723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
42823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	memset(cfg, 0, sizeof(*cfg));
42923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
43023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	cfg->udp_config.family = AF_INET;
43123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
43223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	if (info->attrs[FOU_ATTR_AF]) {
43323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		u8 family = nla_get_u8(info->attrs[FOU_ATTR_AF]);
43423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
43523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		if (family != AF_INET && family != AF_INET6)
43623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert			return -EINVAL;
43723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
43823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		cfg->udp_config.family = family;
43923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	}
44023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
44123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	if (info->attrs[FOU_ATTR_PORT]) {
44223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		u16 port = nla_get_u16(info->attrs[FOU_ATTR_PORT]);
44323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
44423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		cfg->udp_config.local_udp_port = port;
44523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	}
44623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
44723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	if (info->attrs[FOU_ATTR_IPPROTO])
44823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		cfg->protocol = nla_get_u8(info->attrs[FOU_ATTR_IPPROTO]);
44923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
45037dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert	if (info->attrs[FOU_ATTR_TYPE])
45137dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert		cfg->type = nla_get_u8(info->attrs[FOU_ATTR_TYPE]);
45237dd0247797b168ad1cc7f5dbec825a1ee66535bTom Herbert
45323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	return 0;
45423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
45523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
45623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic int fou_nl_cmd_add_port(struct sk_buff *skb, struct genl_info *info)
45723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
45823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct fou_cfg cfg;
45923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	int err;
46023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
46123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	err = parse_nl_config(info, &cfg);
46223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	if (err)
46323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		return err;
46423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
46523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	return fou_create(&init_net, &cfg, NULL);
46623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
46723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
46823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic int fou_nl_cmd_rm_port(struct sk_buff *skb, struct genl_info *info)
46923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
47023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct fou_cfg cfg;
47123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
47223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	parse_nl_config(info, &cfg);
47323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
47423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	return fou_destroy(&init_net, &cfg);
47523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
47623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
47723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic const struct genl_ops fou_nl_ops[] = {
47823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	{
47923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		.cmd = FOU_CMD_ADD,
48023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		.doit = fou_nl_cmd_add_port,
48123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		.policy = fou_nl_policy,
48223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		.flags = GENL_ADMIN_PERM,
48323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	},
48423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	{
48523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		.cmd = FOU_CMD_DEL,
48623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		.doit = fou_nl_cmd_rm_port,
48723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		.policy = fou_nl_policy,
48823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		.flags = GENL_ADMIN_PERM,
48923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	},
49023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert};
49123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
49223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic int __init fou_init(void)
49323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
49423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	int ret;
49523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
49623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	ret = genl_register_family_with_ops(&fou_nl_family,
49723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert					    fou_nl_ops);
49823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
49923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	return ret;
50023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
50123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
50223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertstatic void __exit fou_fini(void)
50323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert{
50423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	struct fou *fou, *next;
50523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
50623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	genl_unregister_family(&fou_nl_family);
50723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
50823461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	/* Close all the FOU sockets */
50923461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
51023461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	spin_lock(&fou_lock);
51123461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	list_for_each_entry_safe(fou, next, &fou_list, list)
51223461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert		fou_release(fou);
51323461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert	spin_unlock(&fou_lock);
51423461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert}
51523461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbert
51623461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertmodule_init(fou_init);
51723461551c00628c3f3fe9cf837bf53cf8f212b63Tom Herbertmodule_exit(fou_fini);
51823461551c00628c3f3fe9cf837bf53cf8f212b63Tom HerbertMODULE_AUTHOR("Tom Herbert <therbert@google.com>");
51923461551c00628c3f3fe9cf837bf53cf8f212b63Tom HerbertMODULE_LICENSE("GPL");
520