162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Bareudp: UDP  tunnel encasulation for different Payload types like
362306a36Sopenharmony_ci * MPLS, NSH, IP, etc.
462306a36Sopenharmony_ci * Copyright (c) 2019 Nokia, Inc.
562306a36Sopenharmony_ci * Authors:  Martin Varghese, <martin.varghese@nokia.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/etherdevice.h>
1362306a36Sopenharmony_ci#include <linux/hash.h>
1462306a36Sopenharmony_ci#include <net/dst_metadata.h>
1562306a36Sopenharmony_ci#include <net/gro_cells.h>
1662306a36Sopenharmony_ci#include <net/rtnetlink.h>
1762306a36Sopenharmony_ci#include <net/protocol.h>
1862306a36Sopenharmony_ci#include <net/ip6_tunnel.h>
1962306a36Sopenharmony_ci#include <net/ip_tunnels.h>
2062306a36Sopenharmony_ci#include <net/udp_tunnel.h>
2162306a36Sopenharmony_ci#include <net/bareudp.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define BAREUDP_BASE_HLEN sizeof(struct udphdr)
2462306a36Sopenharmony_ci#define BAREUDP_IPV4_HLEN (sizeof(struct iphdr) + \
2562306a36Sopenharmony_ci			   sizeof(struct udphdr))
2662306a36Sopenharmony_ci#define BAREUDP_IPV6_HLEN (sizeof(struct ipv6hdr) + \
2762306a36Sopenharmony_ci			   sizeof(struct udphdr))
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic bool log_ecn_error = true;
3062306a36Sopenharmony_cimodule_param(log_ecn_error, bool, 0644);
3162306a36Sopenharmony_ciMODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* per-network namespace private data for this module */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic unsigned int bareudp_net_id;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct bareudp_net {
3862306a36Sopenharmony_ci	struct list_head        bareudp_list;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistruct bareudp_conf {
4262306a36Sopenharmony_ci	__be16 ethertype;
4362306a36Sopenharmony_ci	__be16 port;
4462306a36Sopenharmony_ci	u16 sport_min;
4562306a36Sopenharmony_ci	bool multi_proto_mode;
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Pseudo network device */
4962306a36Sopenharmony_cistruct bareudp_dev {
5062306a36Sopenharmony_ci	struct net         *net;        /* netns for packet i/o */
5162306a36Sopenharmony_ci	struct net_device  *dev;        /* netdev for bareudp tunnel */
5262306a36Sopenharmony_ci	__be16		   ethertype;
5362306a36Sopenharmony_ci	__be16             port;
5462306a36Sopenharmony_ci	u16	           sport_min;
5562306a36Sopenharmony_ci	bool               multi_proto_mode;
5662306a36Sopenharmony_ci	struct socket      __rcu *sock;
5762306a36Sopenharmony_ci	struct list_head   next;        /* bareudp node  on namespace list */
5862306a36Sopenharmony_ci	struct gro_cells   gro_cells;
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int bareudp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct metadata_dst *tun_dst = NULL;
6462306a36Sopenharmony_ci	struct bareudp_dev *bareudp;
6562306a36Sopenharmony_ci	unsigned short family;
6662306a36Sopenharmony_ci	unsigned int len;
6762306a36Sopenharmony_ci	__be16 proto;
6862306a36Sopenharmony_ci	void *oiph;
6962306a36Sopenharmony_ci	int err;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	bareudp = rcu_dereference_sk_user_data(sk);
7262306a36Sopenharmony_ci	if (!bareudp)
7362306a36Sopenharmony_ci		goto drop;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (skb->protocol ==  htons(ETH_P_IP))
7662306a36Sopenharmony_ci		family = AF_INET;
7762306a36Sopenharmony_ci	else
7862306a36Sopenharmony_ci		family = AF_INET6;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (bareudp->ethertype == htons(ETH_P_IP)) {
8162306a36Sopenharmony_ci		__u8 ipversion;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci		if (skb_copy_bits(skb, BAREUDP_BASE_HLEN, &ipversion,
8462306a36Sopenharmony_ci				  sizeof(ipversion))) {
8562306a36Sopenharmony_ci			bareudp->dev->stats.rx_dropped++;
8662306a36Sopenharmony_ci			goto drop;
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci		ipversion >>= 4;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci		if (ipversion == 4) {
9162306a36Sopenharmony_ci			proto = htons(ETH_P_IP);
9262306a36Sopenharmony_ci		} else if (ipversion == 6 && bareudp->multi_proto_mode) {
9362306a36Sopenharmony_ci			proto = htons(ETH_P_IPV6);
9462306a36Sopenharmony_ci		} else {
9562306a36Sopenharmony_ci			bareudp->dev->stats.rx_dropped++;
9662306a36Sopenharmony_ci			goto drop;
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci	} else if (bareudp->ethertype == htons(ETH_P_MPLS_UC)) {
9962306a36Sopenharmony_ci		struct iphdr *tunnel_hdr;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		tunnel_hdr = (struct iphdr *)skb_network_header(skb);
10262306a36Sopenharmony_ci		if (tunnel_hdr->version == 4) {
10362306a36Sopenharmony_ci			if (!ipv4_is_multicast(tunnel_hdr->daddr)) {
10462306a36Sopenharmony_ci				proto = bareudp->ethertype;
10562306a36Sopenharmony_ci			} else if (bareudp->multi_proto_mode &&
10662306a36Sopenharmony_ci				   ipv4_is_multicast(tunnel_hdr->daddr)) {
10762306a36Sopenharmony_ci				proto = htons(ETH_P_MPLS_MC);
10862306a36Sopenharmony_ci			} else {
10962306a36Sopenharmony_ci				bareudp->dev->stats.rx_dropped++;
11062306a36Sopenharmony_ci				goto drop;
11162306a36Sopenharmony_ci			}
11262306a36Sopenharmony_ci		} else {
11362306a36Sopenharmony_ci			int addr_type;
11462306a36Sopenharmony_ci			struct ipv6hdr *tunnel_hdr_v6;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci			tunnel_hdr_v6 = (struct ipv6hdr *)skb_network_header(skb);
11762306a36Sopenharmony_ci			addr_type =
11862306a36Sopenharmony_ci			ipv6_addr_type((struct in6_addr *)&tunnel_hdr_v6->daddr);
11962306a36Sopenharmony_ci			if (!(addr_type & IPV6_ADDR_MULTICAST)) {
12062306a36Sopenharmony_ci				proto = bareudp->ethertype;
12162306a36Sopenharmony_ci			} else if (bareudp->multi_proto_mode &&
12262306a36Sopenharmony_ci				   (addr_type & IPV6_ADDR_MULTICAST)) {
12362306a36Sopenharmony_ci				proto = htons(ETH_P_MPLS_MC);
12462306a36Sopenharmony_ci			} else {
12562306a36Sopenharmony_ci				bareudp->dev->stats.rx_dropped++;
12662306a36Sopenharmony_ci				goto drop;
12762306a36Sopenharmony_ci			}
12862306a36Sopenharmony_ci		}
12962306a36Sopenharmony_ci	} else {
13062306a36Sopenharmony_ci		proto = bareudp->ethertype;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (iptunnel_pull_header(skb, BAREUDP_BASE_HLEN,
13462306a36Sopenharmony_ci				 proto,
13562306a36Sopenharmony_ci				 !net_eq(bareudp->net,
13662306a36Sopenharmony_ci				 dev_net(bareudp->dev)))) {
13762306a36Sopenharmony_ci		bareudp->dev->stats.rx_dropped++;
13862306a36Sopenharmony_ci		goto drop;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci	tun_dst = udp_tun_rx_dst(skb, family, TUNNEL_KEY, 0, 0);
14162306a36Sopenharmony_ci	if (!tun_dst) {
14262306a36Sopenharmony_ci		bareudp->dev->stats.rx_dropped++;
14362306a36Sopenharmony_ci		goto drop;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci	skb_dst_set(skb, &tun_dst->dst);
14662306a36Sopenharmony_ci	skb->dev = bareudp->dev;
14762306a36Sopenharmony_ci	oiph = skb_network_header(skb);
14862306a36Sopenharmony_ci	skb_reset_network_header(skb);
14962306a36Sopenharmony_ci	skb_reset_mac_header(skb);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (!ipv6_mod_enabled() || family == AF_INET)
15262306a36Sopenharmony_ci		err = IP_ECN_decapsulate(oiph, skb);
15362306a36Sopenharmony_ci	else
15462306a36Sopenharmony_ci		err = IP6_ECN_decapsulate(oiph, skb);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (unlikely(err)) {
15762306a36Sopenharmony_ci		if (log_ecn_error) {
15862306a36Sopenharmony_ci			if  (!ipv6_mod_enabled() || family == AF_INET)
15962306a36Sopenharmony_ci				net_info_ratelimited("non-ECT from %pI4 "
16062306a36Sopenharmony_ci						     "with TOS=%#x\n",
16162306a36Sopenharmony_ci						     &((struct iphdr *)oiph)->saddr,
16262306a36Sopenharmony_ci						     ((struct iphdr *)oiph)->tos);
16362306a36Sopenharmony_ci			else
16462306a36Sopenharmony_ci				net_info_ratelimited("non-ECT from %pI6\n",
16562306a36Sopenharmony_ci						     &((struct ipv6hdr *)oiph)->saddr);
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci		if (err > 1) {
16862306a36Sopenharmony_ci			++bareudp->dev->stats.rx_frame_errors;
16962306a36Sopenharmony_ci			++bareudp->dev->stats.rx_errors;
17062306a36Sopenharmony_ci			goto drop;
17162306a36Sopenharmony_ci		}
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	len = skb->len;
17562306a36Sopenharmony_ci	err = gro_cells_receive(&bareudp->gro_cells, skb);
17662306a36Sopenharmony_ci	if (likely(err == NET_RX_SUCCESS))
17762306a36Sopenharmony_ci		dev_sw_netstats_rx_add(bareudp->dev, len);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return 0;
18062306a36Sopenharmony_cidrop:
18162306a36Sopenharmony_ci	/* Consume bad packet */
18262306a36Sopenharmony_ci	kfree_skb(skb);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return 0;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic int bareudp_err_lookup(struct sock *sk, struct sk_buff *skb)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	return 0;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int bareudp_init(struct net_device *dev)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct bareudp_dev *bareudp = netdev_priv(dev);
19562306a36Sopenharmony_ci	int err;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
19862306a36Sopenharmony_ci	if (!dev->tstats)
19962306a36Sopenharmony_ci		return -ENOMEM;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	err = gro_cells_init(&bareudp->gro_cells, dev);
20262306a36Sopenharmony_ci	if (err) {
20362306a36Sopenharmony_ci		free_percpu(dev->tstats);
20462306a36Sopenharmony_ci		return err;
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic void bareudp_uninit(struct net_device *dev)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct bareudp_dev *bareudp = netdev_priv(dev);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	gro_cells_destroy(&bareudp->gro_cells);
21462306a36Sopenharmony_ci	free_percpu(dev->tstats);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic struct socket *bareudp_create_sock(struct net *net, __be16 port)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct udp_port_cfg udp_conf;
22062306a36Sopenharmony_ci	struct socket *sock;
22162306a36Sopenharmony_ci	int err;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	memset(&udp_conf, 0, sizeof(udp_conf));
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (ipv6_mod_enabled())
22662306a36Sopenharmony_ci		udp_conf.family = AF_INET6;
22762306a36Sopenharmony_ci	else
22862306a36Sopenharmony_ci		udp_conf.family = AF_INET;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	udp_conf.local_udp_port = port;
23162306a36Sopenharmony_ci	/* Open UDP socket */
23262306a36Sopenharmony_ci	err = udp_sock_create(net, &udp_conf, &sock);
23362306a36Sopenharmony_ci	if (err < 0)
23462306a36Sopenharmony_ci		return ERR_PTR(err);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	udp_allow_gso(sock->sk);
23762306a36Sopenharmony_ci	return sock;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/* Create new listen socket if needed */
24162306a36Sopenharmony_cistatic int bareudp_socket_create(struct bareudp_dev *bareudp, __be16 port)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct udp_tunnel_sock_cfg tunnel_cfg;
24462306a36Sopenharmony_ci	struct socket *sock;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	sock = bareudp_create_sock(bareudp->net, port);
24762306a36Sopenharmony_ci	if (IS_ERR(sock))
24862306a36Sopenharmony_ci		return PTR_ERR(sock);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/* Mark socket as an encapsulation socket */
25162306a36Sopenharmony_ci	memset(&tunnel_cfg, 0, sizeof(tunnel_cfg));
25262306a36Sopenharmony_ci	tunnel_cfg.sk_user_data = bareudp;
25362306a36Sopenharmony_ci	tunnel_cfg.encap_type = 1;
25462306a36Sopenharmony_ci	tunnel_cfg.encap_rcv = bareudp_udp_encap_recv;
25562306a36Sopenharmony_ci	tunnel_cfg.encap_err_lookup = bareudp_err_lookup;
25662306a36Sopenharmony_ci	tunnel_cfg.encap_destroy = NULL;
25762306a36Sopenharmony_ci	setup_udp_tunnel_sock(bareudp->net, sock, &tunnel_cfg);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	rcu_assign_pointer(bareudp->sock, sock);
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int bareudp_open(struct net_device *dev)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct bareudp_dev *bareudp = netdev_priv(dev);
26662306a36Sopenharmony_ci	int ret = 0;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	ret =  bareudp_socket_create(bareudp, bareudp->port);
26962306a36Sopenharmony_ci	return ret;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void bareudp_sock_release(struct bareudp_dev *bareudp)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct socket *sock;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	sock = bareudp->sock;
27762306a36Sopenharmony_ci	rcu_assign_pointer(bareudp->sock, NULL);
27862306a36Sopenharmony_ci	synchronize_net();
27962306a36Sopenharmony_ci	udp_tunnel_sock_release(sock);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic int bareudp_stop(struct net_device *dev)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct bareudp_dev *bareudp = netdev_priv(dev);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	bareudp_sock_release(bareudp);
28762306a36Sopenharmony_ci	return 0;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int bareudp_xmit_skb(struct sk_buff *skb, struct net_device *dev,
29162306a36Sopenharmony_ci			    struct bareudp_dev *bareudp,
29262306a36Sopenharmony_ci			    const struct ip_tunnel_info *info)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev));
29562306a36Sopenharmony_ci	bool use_cache = ip_tunnel_dst_cache_usable(skb, info);
29662306a36Sopenharmony_ci	struct socket *sock = rcu_dereference(bareudp->sock);
29762306a36Sopenharmony_ci	bool udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM);
29862306a36Sopenharmony_ci	const struct ip_tunnel_key *key = &info->key;
29962306a36Sopenharmony_ci	struct rtable *rt;
30062306a36Sopenharmony_ci	__be16 sport, df;
30162306a36Sopenharmony_ci	int min_headroom;
30262306a36Sopenharmony_ci	__u8 tos, ttl;
30362306a36Sopenharmony_ci	__be32 saddr;
30462306a36Sopenharmony_ci	int err;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (!sock)
30762306a36Sopenharmony_ci		return -ESHUTDOWN;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	rt = ip_route_output_tunnel(skb, dev, bareudp->net, &saddr, info,
31062306a36Sopenharmony_ci				    IPPROTO_UDP, use_cache);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (IS_ERR(rt))
31362306a36Sopenharmony_ci		return PTR_ERR(rt);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	skb_tunnel_check_pmtu(skb, &rt->dst,
31662306a36Sopenharmony_ci			      BAREUDP_IPV4_HLEN + info->options_len, false);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	sport = udp_flow_src_port(bareudp->net, skb,
31962306a36Sopenharmony_ci				  bareudp->sport_min, USHRT_MAX,
32062306a36Sopenharmony_ci				  true);
32162306a36Sopenharmony_ci	tos = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb);
32262306a36Sopenharmony_ci	ttl = key->ttl;
32362306a36Sopenharmony_ci	df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0;
32462306a36Sopenharmony_ci	skb_scrub_packet(skb, xnet);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	err = -ENOSPC;
32762306a36Sopenharmony_ci	if (!skb_pull(skb, skb_network_offset(skb)))
32862306a36Sopenharmony_ci		goto free_dst;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len +
33162306a36Sopenharmony_ci		BAREUDP_BASE_HLEN + info->options_len + sizeof(struct iphdr);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	err = skb_cow_head(skb, min_headroom);
33462306a36Sopenharmony_ci	if (unlikely(err))
33562306a36Sopenharmony_ci		goto free_dst;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	err = udp_tunnel_handle_offloads(skb, udp_sum);
33862306a36Sopenharmony_ci	if (err)
33962306a36Sopenharmony_ci		goto free_dst;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	skb_set_inner_protocol(skb, bareudp->ethertype);
34262306a36Sopenharmony_ci	udp_tunnel_xmit_skb(rt, sock->sk, skb, saddr, info->key.u.ipv4.dst,
34362306a36Sopenharmony_ci			    tos, ttl, df, sport, bareudp->port,
34462306a36Sopenharmony_ci			    !net_eq(bareudp->net, dev_net(bareudp->dev)),
34562306a36Sopenharmony_ci			    !(info->key.tun_flags & TUNNEL_CSUM));
34662306a36Sopenharmony_ci	return 0;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cifree_dst:
34962306a36Sopenharmony_ci	dst_release(&rt->dst);
35062306a36Sopenharmony_ci	return err;
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
35462306a36Sopenharmony_ci			     struct bareudp_dev *bareudp,
35562306a36Sopenharmony_ci			     const struct ip_tunnel_info *info)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev));
35862306a36Sopenharmony_ci	bool use_cache = ip_tunnel_dst_cache_usable(skb, info);
35962306a36Sopenharmony_ci	struct socket *sock  = rcu_dereference(bareudp->sock);
36062306a36Sopenharmony_ci	bool udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM);
36162306a36Sopenharmony_ci	const struct ip_tunnel_key *key = &info->key;
36262306a36Sopenharmony_ci	struct dst_entry *dst = NULL;
36362306a36Sopenharmony_ci	struct in6_addr saddr, daddr;
36462306a36Sopenharmony_ci	int min_headroom;
36562306a36Sopenharmony_ci	__u8 prio, ttl;
36662306a36Sopenharmony_ci	__be16 sport;
36762306a36Sopenharmony_ci	int err;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (!sock)
37062306a36Sopenharmony_ci		return -ESHUTDOWN;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	dst = ip6_dst_lookup_tunnel(skb, dev, bareudp->net, sock, &saddr, info,
37362306a36Sopenharmony_ci				    IPPROTO_UDP, use_cache);
37462306a36Sopenharmony_ci	if (IS_ERR(dst))
37562306a36Sopenharmony_ci		return PTR_ERR(dst);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	skb_tunnel_check_pmtu(skb, dst, BAREUDP_IPV6_HLEN + info->options_len,
37862306a36Sopenharmony_ci			      false);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	sport = udp_flow_src_port(bareudp->net, skb,
38162306a36Sopenharmony_ci				  bareudp->sport_min, USHRT_MAX,
38262306a36Sopenharmony_ci				  true);
38362306a36Sopenharmony_ci	prio = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb);
38462306a36Sopenharmony_ci	ttl = key->ttl;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	skb_scrub_packet(skb, xnet);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	err = -ENOSPC;
38962306a36Sopenharmony_ci	if (!skb_pull(skb, skb_network_offset(skb)))
39062306a36Sopenharmony_ci		goto free_dst;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len +
39362306a36Sopenharmony_ci		BAREUDP_BASE_HLEN + info->options_len + sizeof(struct ipv6hdr);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	err = skb_cow_head(skb, min_headroom);
39662306a36Sopenharmony_ci	if (unlikely(err))
39762306a36Sopenharmony_ci		goto free_dst;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	err = udp_tunnel_handle_offloads(skb, udp_sum);
40062306a36Sopenharmony_ci	if (err)
40162306a36Sopenharmony_ci		goto free_dst;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	daddr = info->key.u.ipv6.dst;
40462306a36Sopenharmony_ci	udp_tunnel6_xmit_skb(dst, sock->sk, skb, dev,
40562306a36Sopenharmony_ci			     &saddr, &daddr, prio, ttl,
40662306a36Sopenharmony_ci			     info->key.label, sport, bareudp->port,
40762306a36Sopenharmony_ci			     !(info->key.tun_flags & TUNNEL_CSUM));
40862306a36Sopenharmony_ci	return 0;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cifree_dst:
41162306a36Sopenharmony_ci	dst_release(dst);
41262306a36Sopenharmony_ci	return err;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic bool bareudp_proto_valid(struct bareudp_dev *bareudp, __be16 proto)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	if (bareudp->ethertype == proto)
41862306a36Sopenharmony_ci		return true;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	if (!bareudp->multi_proto_mode)
42162306a36Sopenharmony_ci		return false;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (bareudp->ethertype == htons(ETH_P_MPLS_UC) &&
42462306a36Sopenharmony_ci	    proto == htons(ETH_P_MPLS_MC))
42562306a36Sopenharmony_ci		return true;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (bareudp->ethertype == htons(ETH_P_IP) &&
42862306a36Sopenharmony_ci	    proto == htons(ETH_P_IPV6))
42962306a36Sopenharmony_ci		return true;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return false;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic netdev_tx_t bareudp_xmit(struct sk_buff *skb, struct net_device *dev)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	struct bareudp_dev *bareudp = netdev_priv(dev);
43762306a36Sopenharmony_ci	struct ip_tunnel_info *info = NULL;
43862306a36Sopenharmony_ci	int err;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (!bareudp_proto_valid(bareudp, skb->protocol)) {
44162306a36Sopenharmony_ci		err = -EINVAL;
44262306a36Sopenharmony_ci		goto tx_error;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	info = skb_tunnel_info(skb);
44662306a36Sopenharmony_ci	if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) {
44762306a36Sopenharmony_ci		err = -EINVAL;
44862306a36Sopenharmony_ci		goto tx_error;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	rcu_read_lock();
45262306a36Sopenharmony_ci	if (ipv6_mod_enabled() && info->mode & IP_TUNNEL_INFO_IPV6)
45362306a36Sopenharmony_ci		err = bareudp6_xmit_skb(skb, dev, bareudp, info);
45462306a36Sopenharmony_ci	else
45562306a36Sopenharmony_ci		err = bareudp_xmit_skb(skb, dev, bareudp, info);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	rcu_read_unlock();
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (likely(!err))
46062306a36Sopenharmony_ci		return NETDEV_TX_OK;
46162306a36Sopenharmony_citx_error:
46262306a36Sopenharmony_ci	dev_kfree_skb(skb);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (err == -ELOOP)
46562306a36Sopenharmony_ci		dev->stats.collisions++;
46662306a36Sopenharmony_ci	else if (err == -ENETUNREACH)
46762306a36Sopenharmony_ci		dev->stats.tx_carrier_errors++;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	dev->stats.tx_errors++;
47062306a36Sopenharmony_ci	return NETDEV_TX_OK;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cistatic int bareudp_fill_metadata_dst(struct net_device *dev,
47462306a36Sopenharmony_ci				     struct sk_buff *skb)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	struct ip_tunnel_info *info = skb_tunnel_info(skb);
47762306a36Sopenharmony_ci	struct bareudp_dev *bareudp = netdev_priv(dev);
47862306a36Sopenharmony_ci	bool use_cache;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	use_cache = ip_tunnel_dst_cache_usable(skb, info);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (!ipv6_mod_enabled() || ip_tunnel_info_af(info) == AF_INET) {
48362306a36Sopenharmony_ci		struct rtable *rt;
48462306a36Sopenharmony_ci		__be32 saddr;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci		rt = ip_route_output_tunnel(skb, dev, bareudp->net, &saddr,
48762306a36Sopenharmony_ci					    info, IPPROTO_UDP, use_cache);
48862306a36Sopenharmony_ci		if (IS_ERR(rt))
48962306a36Sopenharmony_ci			return PTR_ERR(rt);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		ip_rt_put(rt);
49262306a36Sopenharmony_ci		info->key.u.ipv4.src = saddr;
49362306a36Sopenharmony_ci	} else if (ip_tunnel_info_af(info) == AF_INET6) {
49462306a36Sopenharmony_ci		struct dst_entry *dst;
49562306a36Sopenharmony_ci		struct in6_addr saddr;
49662306a36Sopenharmony_ci		struct socket *sock = rcu_dereference(bareudp->sock);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		dst = ip6_dst_lookup_tunnel(skb, dev, bareudp->net, sock,
49962306a36Sopenharmony_ci					    &saddr, info, IPPROTO_UDP,
50062306a36Sopenharmony_ci					    use_cache);
50162306a36Sopenharmony_ci		if (IS_ERR(dst))
50262306a36Sopenharmony_ci			return PTR_ERR(dst);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		dst_release(dst);
50562306a36Sopenharmony_ci		info->key.u.ipv6.src = saddr;
50662306a36Sopenharmony_ci	} else {
50762306a36Sopenharmony_ci		return -EINVAL;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	info->key.tp_src = udp_flow_src_port(bareudp->net, skb,
51162306a36Sopenharmony_ci					     bareudp->sport_min,
51262306a36Sopenharmony_ci			USHRT_MAX, true);
51362306a36Sopenharmony_ci	info->key.tp_dst = bareudp->port;
51462306a36Sopenharmony_ci	return 0;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic const struct net_device_ops bareudp_netdev_ops = {
51862306a36Sopenharmony_ci	.ndo_init               = bareudp_init,
51962306a36Sopenharmony_ci	.ndo_uninit             = bareudp_uninit,
52062306a36Sopenharmony_ci	.ndo_open               = bareudp_open,
52162306a36Sopenharmony_ci	.ndo_stop               = bareudp_stop,
52262306a36Sopenharmony_ci	.ndo_start_xmit         = bareudp_xmit,
52362306a36Sopenharmony_ci	.ndo_get_stats64        = dev_get_tstats64,
52462306a36Sopenharmony_ci	.ndo_fill_metadata_dst  = bareudp_fill_metadata_dst,
52562306a36Sopenharmony_ci};
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic const struct nla_policy bareudp_policy[IFLA_BAREUDP_MAX + 1] = {
52862306a36Sopenharmony_ci	[IFLA_BAREUDP_PORT]                = { .type = NLA_U16 },
52962306a36Sopenharmony_ci	[IFLA_BAREUDP_ETHERTYPE]	   = { .type = NLA_U16 },
53062306a36Sopenharmony_ci	[IFLA_BAREUDP_SRCPORT_MIN]         = { .type = NLA_U16 },
53162306a36Sopenharmony_ci	[IFLA_BAREUDP_MULTIPROTO_MODE]     = { .type = NLA_FLAG },
53262306a36Sopenharmony_ci};
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci/* Info for udev, that this is a virtual tunnel endpoint */
53562306a36Sopenharmony_cistatic const struct device_type bareudp_type = {
53662306a36Sopenharmony_ci	.name = "bareudp",
53762306a36Sopenharmony_ci};
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci/* Initialize the device structure. */
54062306a36Sopenharmony_cistatic void bareudp_setup(struct net_device *dev)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	dev->netdev_ops = &bareudp_netdev_ops;
54362306a36Sopenharmony_ci	dev->needs_free_netdev = true;
54462306a36Sopenharmony_ci	SET_NETDEV_DEVTYPE(dev, &bareudp_type);
54562306a36Sopenharmony_ci	dev->features    |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST;
54662306a36Sopenharmony_ci	dev->features    |= NETIF_F_RXCSUM;
54762306a36Sopenharmony_ci	dev->features    |= NETIF_F_LLTX;
54862306a36Sopenharmony_ci	dev->features    |= NETIF_F_GSO_SOFTWARE;
54962306a36Sopenharmony_ci	dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST;
55062306a36Sopenharmony_ci	dev->hw_features |= NETIF_F_RXCSUM;
55162306a36Sopenharmony_ci	dev->hw_features |= NETIF_F_GSO_SOFTWARE;
55262306a36Sopenharmony_ci	dev->hard_header_len = 0;
55362306a36Sopenharmony_ci	dev->addr_len = 0;
55462306a36Sopenharmony_ci	dev->mtu = ETH_DATA_LEN;
55562306a36Sopenharmony_ci	dev->min_mtu = IPV4_MIN_MTU;
55662306a36Sopenharmony_ci	dev->max_mtu = IP_MAX_MTU - BAREUDP_BASE_HLEN;
55762306a36Sopenharmony_ci	dev->type = ARPHRD_NONE;
55862306a36Sopenharmony_ci	netif_keep_dst(dev);
55962306a36Sopenharmony_ci	dev->priv_flags |= IFF_NO_QUEUE;
56062306a36Sopenharmony_ci	dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic int bareudp_validate(struct nlattr *tb[], struct nlattr *data[],
56462306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	if (!data) {
56762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
56862306a36Sopenharmony_ci			       "Not enough attributes provided to perform the operation");
56962306a36Sopenharmony_ci		return -EINVAL;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci	return 0;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic int bareudp2info(struct nlattr *data[], struct bareudp_conf *conf,
57562306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	memset(conf, 0, sizeof(*conf));
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (!data[IFLA_BAREUDP_PORT]) {
58062306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "port not specified");
58162306a36Sopenharmony_ci		return -EINVAL;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci	if (!data[IFLA_BAREUDP_ETHERTYPE]) {
58462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "ethertype not specified");
58562306a36Sopenharmony_ci		return -EINVAL;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	conf->port = nla_get_u16(data[IFLA_BAREUDP_PORT]);
58962306a36Sopenharmony_ci	conf->ethertype = nla_get_u16(data[IFLA_BAREUDP_ETHERTYPE]);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (data[IFLA_BAREUDP_SRCPORT_MIN])
59262306a36Sopenharmony_ci		conf->sport_min =  nla_get_u16(data[IFLA_BAREUDP_SRCPORT_MIN]);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (data[IFLA_BAREUDP_MULTIPROTO_MODE])
59562306a36Sopenharmony_ci		conf->multi_proto_mode = true;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	return 0;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_cistatic struct bareudp_dev *bareudp_find_dev(struct bareudp_net *bn,
60162306a36Sopenharmony_ci					    const struct bareudp_conf *conf)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	struct bareudp_dev *bareudp, *t = NULL;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	list_for_each_entry(bareudp, &bn->bareudp_list, next) {
60662306a36Sopenharmony_ci		if (conf->port == bareudp->port)
60762306a36Sopenharmony_ci			t = bareudp;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci	return t;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int bareudp_configure(struct net *net, struct net_device *dev,
61362306a36Sopenharmony_ci			     struct bareudp_conf *conf,
61462306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
61562306a36Sopenharmony_ci{
61662306a36Sopenharmony_ci	struct bareudp_net *bn = net_generic(net, bareudp_net_id);
61762306a36Sopenharmony_ci	struct bareudp_dev *t, *bareudp = netdev_priv(dev);
61862306a36Sopenharmony_ci	int err;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	bareudp->net = net;
62162306a36Sopenharmony_ci	bareudp->dev = dev;
62262306a36Sopenharmony_ci	t = bareudp_find_dev(bn, conf);
62362306a36Sopenharmony_ci	if (t) {
62462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Another bareudp device using the same port already exists");
62562306a36Sopenharmony_ci		return -EBUSY;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (conf->multi_proto_mode &&
62962306a36Sopenharmony_ci	    (conf->ethertype != htons(ETH_P_MPLS_UC) &&
63062306a36Sopenharmony_ci	     conf->ethertype != htons(ETH_P_IP))) {
63162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Cannot set multiproto mode for this ethertype (only IPv4 and unicast MPLS are supported)");
63262306a36Sopenharmony_ci		return -EINVAL;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	bareudp->port = conf->port;
63662306a36Sopenharmony_ci	bareudp->ethertype = conf->ethertype;
63762306a36Sopenharmony_ci	bareudp->sport_min = conf->sport_min;
63862306a36Sopenharmony_ci	bareudp->multi_proto_mode = conf->multi_proto_mode;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	err = register_netdevice(dev);
64162306a36Sopenharmony_ci	if (err)
64262306a36Sopenharmony_ci		return err;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	list_add(&bareudp->next, &bn->bareudp_list);
64562306a36Sopenharmony_ci	return 0;
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic int bareudp_link_config(struct net_device *dev,
64962306a36Sopenharmony_ci			       struct nlattr *tb[])
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	int err;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (tb[IFLA_MTU]) {
65462306a36Sopenharmony_ci		err = dev_set_mtu(dev, nla_get_u32(tb[IFLA_MTU]));
65562306a36Sopenharmony_ci		if (err)
65662306a36Sopenharmony_ci			return err;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci	return 0;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic void bareudp_dellink(struct net_device *dev, struct list_head *head)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	struct bareudp_dev *bareudp = netdev_priv(dev);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	list_del(&bareudp->next);
66662306a36Sopenharmony_ci	unregister_netdevice_queue(dev, head);
66762306a36Sopenharmony_ci}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_cistatic int bareudp_newlink(struct net *net, struct net_device *dev,
67062306a36Sopenharmony_ci			   struct nlattr *tb[], struct nlattr *data[],
67162306a36Sopenharmony_ci			   struct netlink_ext_ack *extack)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	struct bareudp_conf conf;
67462306a36Sopenharmony_ci	int err;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	err = bareudp2info(data, &conf, extack);
67762306a36Sopenharmony_ci	if (err)
67862306a36Sopenharmony_ci		return err;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	err = bareudp_configure(net, dev, &conf, extack);
68162306a36Sopenharmony_ci	if (err)
68262306a36Sopenharmony_ci		return err;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	err = bareudp_link_config(dev, tb);
68562306a36Sopenharmony_ci	if (err)
68662306a36Sopenharmony_ci		goto err_unconfig;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	return 0;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_cierr_unconfig:
69162306a36Sopenharmony_ci	bareudp_dellink(dev, NULL);
69262306a36Sopenharmony_ci	return err;
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_cistatic size_t bareudp_get_size(const struct net_device *dev)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	return  nla_total_size(sizeof(__be16)) +  /* IFLA_BAREUDP_PORT */
69862306a36Sopenharmony_ci		nla_total_size(sizeof(__be16)) +  /* IFLA_BAREUDP_ETHERTYPE */
69962306a36Sopenharmony_ci		nla_total_size(sizeof(__u16))  +  /* IFLA_BAREUDP_SRCPORT_MIN */
70062306a36Sopenharmony_ci		nla_total_size(0)              +  /* IFLA_BAREUDP_MULTIPROTO_MODE */
70162306a36Sopenharmony_ci		0;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int bareudp_fill_info(struct sk_buff *skb, const struct net_device *dev)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct bareudp_dev *bareudp = netdev_priv(dev);
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (nla_put_be16(skb, IFLA_BAREUDP_PORT, bareudp->port))
70962306a36Sopenharmony_ci		goto nla_put_failure;
71062306a36Sopenharmony_ci	if (nla_put_be16(skb, IFLA_BAREUDP_ETHERTYPE, bareudp->ethertype))
71162306a36Sopenharmony_ci		goto nla_put_failure;
71262306a36Sopenharmony_ci	if (nla_put_u16(skb, IFLA_BAREUDP_SRCPORT_MIN, bareudp->sport_min))
71362306a36Sopenharmony_ci		goto nla_put_failure;
71462306a36Sopenharmony_ci	if (bareudp->multi_proto_mode &&
71562306a36Sopenharmony_ci	    nla_put_flag(skb, IFLA_BAREUDP_MULTIPROTO_MODE))
71662306a36Sopenharmony_ci		goto nla_put_failure;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	return 0;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_cinla_put_failure:
72162306a36Sopenharmony_ci	return -EMSGSIZE;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic struct rtnl_link_ops bareudp_link_ops __read_mostly = {
72562306a36Sopenharmony_ci	.kind           = "bareudp",
72662306a36Sopenharmony_ci	.maxtype        = IFLA_BAREUDP_MAX,
72762306a36Sopenharmony_ci	.policy         = bareudp_policy,
72862306a36Sopenharmony_ci	.priv_size      = sizeof(struct bareudp_dev),
72962306a36Sopenharmony_ci	.setup          = bareudp_setup,
73062306a36Sopenharmony_ci	.validate       = bareudp_validate,
73162306a36Sopenharmony_ci	.newlink        = bareudp_newlink,
73262306a36Sopenharmony_ci	.dellink        = bareudp_dellink,
73362306a36Sopenharmony_ci	.get_size       = bareudp_get_size,
73462306a36Sopenharmony_ci	.fill_info      = bareudp_fill_info,
73562306a36Sopenharmony_ci};
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic __net_init int bareudp_init_net(struct net *net)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	struct bareudp_net *bn = net_generic(net, bareudp_net_id);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	INIT_LIST_HEAD(&bn->bareudp_list);
74262306a36Sopenharmony_ci	return 0;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic void bareudp_destroy_tunnels(struct net *net, struct list_head *head)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct bareudp_net *bn = net_generic(net, bareudp_net_id);
74862306a36Sopenharmony_ci	struct bareudp_dev *bareudp, *next;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	list_for_each_entry_safe(bareudp, next, &bn->bareudp_list, next)
75162306a36Sopenharmony_ci		unregister_netdevice_queue(bareudp->dev, head);
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic void __net_exit bareudp_exit_batch_net(struct list_head *net_list)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	struct net *net;
75762306a36Sopenharmony_ci	LIST_HEAD(list);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	rtnl_lock();
76062306a36Sopenharmony_ci	list_for_each_entry(net, net_list, exit_list)
76162306a36Sopenharmony_ci		bareudp_destroy_tunnels(net, &list);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	/* unregister the devices gathered above */
76462306a36Sopenharmony_ci	unregister_netdevice_many(&list);
76562306a36Sopenharmony_ci	rtnl_unlock();
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic struct pernet_operations bareudp_net_ops = {
76962306a36Sopenharmony_ci	.init = bareudp_init_net,
77062306a36Sopenharmony_ci	.exit_batch = bareudp_exit_batch_net,
77162306a36Sopenharmony_ci	.id   = &bareudp_net_id,
77262306a36Sopenharmony_ci	.size = sizeof(struct bareudp_net),
77362306a36Sopenharmony_ci};
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic int __init bareudp_init_module(void)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	int rc;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	rc = register_pernet_subsys(&bareudp_net_ops);
78062306a36Sopenharmony_ci	if (rc)
78162306a36Sopenharmony_ci		goto out1;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	rc = rtnl_link_register(&bareudp_link_ops);
78462306a36Sopenharmony_ci	if (rc)
78562306a36Sopenharmony_ci		goto out2;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return 0;
78862306a36Sopenharmony_ciout2:
78962306a36Sopenharmony_ci	unregister_pernet_subsys(&bareudp_net_ops);
79062306a36Sopenharmony_ciout1:
79162306a36Sopenharmony_ci	return rc;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_cilate_initcall(bareudp_init_module);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_cistatic void __exit bareudp_cleanup_module(void)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	rtnl_link_unregister(&bareudp_link_ops);
79862306a36Sopenharmony_ci	unregister_pernet_subsys(&bareudp_net_ops);
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_cimodule_exit(bareudp_cleanup_module);
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("bareudp");
80362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
80462306a36Sopenharmony_ciMODULE_AUTHOR("Martin Varghese <martin.varghese@nokia.com>");
80562306a36Sopenharmony_ciMODULE_DESCRIPTION("Interface driver for UDP encapsulated traffic");
806