Commit 60088891 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'amt-driver'

Taehee Yoo says:

====================
amt: add initial driver for Automatic Multicast Tunneling (AMT)

This is an implementation of AMT(Automatic Multicast Tunneling), RFC 7450.
https://datatracker.ietf.org/doc/html/rfc7450



This implementation supports IGMPv2, IGMPv3, MLDv1, MLDv2, and IPv4
underlay.

 Summary of RFC 7450
The purpose of this protocol is to provide multicast tunneling.
The main use-case of this protocol is to provide delivery multicast
traffic from a multicast-enabled network to sites that lack multicast
connectivity to the source network.
There are two roles in AMT protocol, Gateway, and Relay.
The main purpose of Gateway mode is to forward multicast listening
information(IGMP, MLD) to the source.
The main purpose of Relay mode is to forward multicast data to listeners.
These multicast traffics(IGMP, MLD, multicast data packets) are tunneled.

Listeners are located behind Gateway endpoint.
But gateway itself can be a listener too.
Senders are located behind Relay endpoint.

    ___________       _________       _______       ________
   |           |     |         |     |       |     |        |
   | Listeners <-----> Gateway <-----> Relay <-----> Source |
   |___________|     |_________|     |_______|     |________|
      IGMP/MLD---------(encap)----------->
         <-------------(decap)--------(encap)------Multicast Data

 Usage of AMT interface
1. Create gateway interface
ip link add amtg type amt mode gateway local 10.0.0.1 discovery 10.0.0.2 \
dev gw1_rt gateway_port 2268 relay_port 2268

2. Create Relay interface
ip link add amtr type amt mode relay local 10.0.0.2 dev relay_rt \
relay_port 2268 max_tunnels 4

v1 -> v2:
 - Eliminate sparse warnings.
   - Use bool type instead of __be16 for identifying v4/v6 protocol.

v2 -> v3:
 - Fix compile warning due to unsed variable.
 - Add missing spinlock comment.
 - Update help message of amt in Kconfig.

v3 -> v4:
 - Split patch.
 - Use CHECKSUM_NONE instead of CHECKSUM_UNNECESSARY.
 - Fix compile error.

v4 -> v5:
 - Remove unnecessary rcu_read_lock().
 - Remove unnecessary amt_change_mtu().
 - Change netlink error message.
 - Add validation for IFLA_AMT_LOCAL_IP and IFLA_AMT_DISCOVERY_IP.
 - Add comments in amt.h.
 - Add missing dev_put() in error path of amt_newlink().
 - Fix typo.
 - Add BUILD_BUG_ON() in amt_smb_cb().
 - Use macro instead of magic values.
 - Use kzalloc() instead of kmalloc().
 - Add selftest script.

v5 -> v6:
 - Reset remote_ip in amt_dev_stop().

v6 -> v7:
 - Fix compile error.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 741948ff c08e8bae
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -1020,6 +1020,14 @@ S: Maintained
F:	Documentation/devicetree/bindings/iio/light/ams,as73211.yaml
F:	drivers/iio/light/as73211.c
AMT (Automatic Multicast Tunneling)
M:	Taehee Yoo <ap420073@gmail.com>
L:	netdev@vger.kernel.org
S:	Maintained
T:	git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git
T:	git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
F:	drivers/net/amt.c
ANALOG DEVICES INC AD7192 DRIVER
M:	Alexandru Tachici <alexandru.tachici@analog.com>
L:	linux-iio@vger.kernel.org
+16 −0
Original line number Diff line number Diff line
@@ -291,6 +291,22 @@ config GTP
	  To compile this drivers as a module, choose M here: the module
	  will be called gtp.

config AMT
	tristate "Automatic Multicast Tunneling (AMT)"
	depends on INET && IP_MULTICAST
	select NET_UDP_TUNNEL
	help
	  This allows one to create AMT(Automatic Multicast Tunneling)
	  virtual interfaces that provide multicast tunneling.
	  There are two roles, Gateway, and Relay.
	  Gateway Encapsulates IGMP/MLD traffic from listeners to the Relay.
	  Gateway Decapsulates multicast traffic from the Relay to Listeners.
	  Relay Encapsulates multicast traffic from Sources to Gateway.
	  Relay Decapsulates IGMP/MLD traffic from Gateway.

	  To compile this drivers as a module, choose M here: the module
	  will be called amt.

config MACSEC
	tristate "IEEE 802.1AE MAC-level encryption (MACsec)"
	select CRYPTO
+1 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ obj-$(CONFIG_WIREGUARD) += wireguard/
obj-$(CONFIG_EQUALIZER) += eql.o
obj-$(CONFIG_IFB) += ifb.o
obj-$(CONFIG_MACSEC) += macsec.o
obj-$(CONFIG_AMT) += amt.o
obj-$(CONFIG_MACVLAN) += macvlan.o
obj-$(CONFIG_MACVTAP) += macvtap.o
obj-$(CONFIG_MII) += mii.o

drivers/net/amt.c

0 → 100644
+3296 −0

File added.

Preview size limit exceeded, changes collapsed.

include/net/amt.h

0 → 100644
+385 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * Copyright (c) 2021 Taehee Yoo <ap420073@gmail.com>
 */
#ifndef _NET_AMT_H_
#define _NET_AMT_H_

#include <linux/siphash.h>
#include <linux/jhash.h>

enum amt_msg_type {
	AMT_MSG_DISCOVERY = 1,
	AMT_MSG_ADVERTISEMENT,
	AMT_MSG_REQUEST,
	AMT_MSG_MEMBERSHIP_QUERY,
	AMT_MSG_MEMBERSHIP_UPDATE,
	AMT_MSG_MULTICAST_DATA,
	AMT_MSG_TEARDOWM,
	__AMT_MSG_MAX,
};

#define AMT_MSG_MAX (__AMT_MSG_MAX - 1)

enum amt_ops {
	/* A*B */
	AMT_OPS_INT,
	/* A+B */
	AMT_OPS_UNI,
	/* A-B */
	AMT_OPS_SUB,
	/* B-A */
	AMT_OPS_SUB_REV,
	__AMT_OPS_MAX,
};

#define AMT_OPS_MAX (__AMT_OPS_MAX - 1)

enum amt_filter {
	AMT_FILTER_FWD,
	AMT_FILTER_D_FWD,
	AMT_FILTER_FWD_NEW,
	AMT_FILTER_D_FWD_NEW,
	AMT_FILTER_ALL,
	AMT_FILTER_NONE_NEW,
	AMT_FILTER_BOTH,
	AMT_FILTER_BOTH_NEW,
	__AMT_FILTER_MAX,
};

#define AMT_FILTER_MAX (__AMT_FILTER_MAX - 1)

enum amt_act {
	AMT_ACT_GMI,
	AMT_ACT_GMI_ZERO,
	AMT_ACT_GT,
	AMT_ACT_STATUS_FWD_NEW,
	AMT_ACT_STATUS_D_FWD_NEW,
	AMT_ACT_STATUS_NONE_NEW,
	__AMT_ACT_MAX,
};

#define AMT_ACT_MAX (__AMT_ACT_MAX - 1)

enum amt_status {
	AMT_STATUS_INIT,
	AMT_STATUS_SENT_DISCOVERY,
	AMT_STATUS_RECEIVED_DISCOVERY,
	AMT_STATUS_SENT_ADVERTISEMENT,
	AMT_STATUS_RECEIVED_ADVERTISEMENT,
	AMT_STATUS_SENT_REQUEST,
	AMT_STATUS_RECEIVED_REQUEST,
	AMT_STATUS_SENT_QUERY,
	AMT_STATUS_RECEIVED_QUERY,
	AMT_STATUS_SENT_UPDATE,
	AMT_STATUS_RECEIVED_UPDATE,
	__AMT_STATUS_MAX,
};

#define AMT_STATUS_MAX (__AMT_STATUS_MAX - 1)

struct amt_header {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u8 type:4,
	   version:4;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u8 version:4,
	   type:4;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
} __packed;

struct amt_header_discovery {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u32	type:4,
		version:4,
		reserved:24;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u32	version:4,
		type:4,
		reserved:24;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
	__be32	nonce;
} __packed;

struct amt_header_advertisement {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u32	type:4,
		version:4,
		reserved:24;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u32	version:4,
		type:4,
		reserved:24;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
	__be32	nonce;
	__be32	ip4;
} __packed;

struct amt_header_request {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u32	type:4,
		version:4,
		reserved1:7,
		p:1,
		reserved2:16;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u32	version:4,
		type:4,
		p:1,
		reserved1:7,
		reserved2:16;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
	__be32	nonce;
} __packed;

struct amt_header_membership_query {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u64	type:4,
		version:4,
		reserved:6,
		l:1,
		g:1,
		response_mac:48;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u64	version:4,
		type:4,
		g:1,
		l:1,
		reserved:6,
		response_mac:48;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
	__be32	nonce;
} __packed;

struct amt_header_membership_update {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u64	type:4,
		version:4,
		reserved:8,
		response_mac:48;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u64	version:4,
		type:4,
		reserved:8,
		response_mac:48;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
	__be32	nonce;
} __packed;

struct amt_header_mcast_data {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u16	type:4,
		version:4,
		reserved:8;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u16	version:4,
		type:4,
		reserved:8;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
} __packed;

struct amt_headers {
	union {
		struct amt_header_discovery discovery;
		struct amt_header_advertisement advertisement;
		struct amt_header_request request;
		struct amt_header_membership_query query;
		struct amt_header_membership_update update;
		struct amt_header_mcast_data data;
	};
} __packed;

struct amt_gw_headers {
	union {
		struct amt_header_discovery discovery;
		struct amt_header_request request;
		struct amt_header_membership_update update;
	};
} __packed;

struct amt_relay_headers {
	union {
		struct amt_header_advertisement advertisement;
		struct amt_header_membership_query query;
		struct amt_header_mcast_data data;
	};
} __packed;

struct amt_skb_cb {
	struct amt_tunnel_list *tunnel;
};

struct amt_tunnel_list {
	struct list_head	list;
	/* Protect All resources under an amt_tunne_list */
	spinlock_t		lock;
	struct amt_dev		*amt;
	u32			nr_groups;
	u32			nr_sources;
	enum amt_status		status;
	struct delayed_work	gc_wq;
	__be16			source_port;
	__be32			ip4;
	__be32			nonce;
	siphash_key_t		key;
	u64			mac:48,
				reserved:16;
	struct rcu_head		rcu;
	struct hlist_head	groups[];
};

union amt_addr {
	__be32			ip4;
#if IS_ENABLED(CONFIG_IPV6)
	struct in6_addr		ip6;
#endif
};

/* RFC 3810
 *
 * When the router is in EXCLUDE mode, the router state is represented
 * by the notation EXCLUDE (X,Y), where X is called the "Requested List"
 * and Y is called the "Exclude List".  All sources, except those from
 * the Exclude List, will be forwarded by the router
 */
enum amt_source_status {
	AMT_SOURCE_STATUS_NONE,
	/* Node of Requested List */
	AMT_SOURCE_STATUS_FWD,
	/* Node of Exclude List */
	AMT_SOURCE_STATUS_D_FWD,
};

/* protected by gnode->lock */
struct amt_source_node {
	struct hlist_node	node;
	struct amt_group_node	*gnode;
	struct delayed_work     source_timer;
	union amt_addr		source_addr;
	enum amt_source_status	status;
#define AMT_SOURCE_OLD	0
#define AMT_SOURCE_NEW	1
	u8			flags;
	struct rcu_head		rcu;
};

/* Protected by amt_tunnel_list->lock */
struct amt_group_node {
	struct amt_dev		*amt;
	union amt_addr		group_addr;
	union amt_addr		host_addr;
	bool			v6;
	u8			filter_mode;
	u32			nr_sources;
	struct amt_tunnel_list	*tunnel_list;
	struct hlist_node	node;
	struct delayed_work     group_timer;
	struct rcu_head		rcu;
	struct hlist_head	sources[];
};

struct amt_dev {
	struct net_device       *dev;
	struct net_device       *stream_dev;
	struct net		*net;
	/* Global lock for amt device */
	spinlock_t		lock;
	/* Used only in relay mode */
	struct list_head        tunnel_list;
	struct gro_cells	gro_cells;

	/* Protected by RTNL */
	struct delayed_work     discovery_wq;
	/* Protected by RTNL */
	struct delayed_work     req_wq;
	/* Protected by RTNL */
	struct delayed_work     secret_wq;
	/* AMT status */
	enum amt_status		status;
	/* Generated key */
	siphash_key_t		key;
	struct socket	  __rcu *sock;
	u32			max_groups;
	u32			max_sources;
	u32			hash_buckets;
	u32			hash_seed;
	/* Default 128 */
	u32                     max_tunnels;
	/* Default 128 */
	u32                     nr_tunnels;
	/* Gateway or Relay mode */
	u32                     mode;
	/* Default 2268 */
	__be16			relay_port;
	/* Default 2268 */
	__be16			gw_port;
	/* Outer local ip */
	__be32			local_ip;
	/* Outer remote ip */
	__be32			remote_ip;
	/* Outer discovery ip */
	__be32			discovery_ip;
	/* Only used in gateway mode */
	__be32			nonce;
	/* Gateway sent request and received query */
	bool			ready4;
	bool			ready6;
	u8			req_cnt;
	u8			qi;
	u64			qrv;
	u64			qri;
	/* Used only in gateway mode */
	u64			mac:48,
				reserved:16;
};

#define AMT_TOS			0xc0
#define AMT_IPHDR_OPTS		4
#define AMT_IP6HDR_OPTS		8
#define AMT_GC_INTERVAL		(30 * 1000)
#define AMT_MAX_GROUP		32
#define AMT_MAX_SOURCE		128
#define AMT_HSIZE_SHIFT		8
#define AMT_HSIZE		(1 << AMT_HSIZE_SHIFT)

#define AMT_DISCOVERY_TIMEOUT	5000
#define AMT_INIT_REQ_TIMEOUT	1
#define AMT_INIT_QUERY_INTERVAL	125
#define AMT_MAX_REQ_TIMEOUT	120
#define AMT_MAX_REQ_COUNT	3
#define AMT_SECRET_TIMEOUT	60000
#define IANA_AMT_UDP_PORT	2268
#define AMT_MAX_TUNNELS         128
#define AMT_MAX_REQS		128
#define AMT_GW_HLEN (sizeof(struct iphdr) + \
		     sizeof(struct udphdr) + \
		     sizeof(struct amt_gw_headers))
#define AMT_RELAY_HLEN (sizeof(struct iphdr) + \
		     sizeof(struct udphdr) + \
		     sizeof(struct amt_relay_headers))

static inline bool netif_is_amt(const struct net_device *dev)
{
	return dev->rtnl_link_ops && !strcmp(dev->rtnl_link_ops->kind, "amt");
}

static inline u64 amt_gmi(const struct amt_dev *amt)
{
	return ((amt->qrv * amt->qi) + amt->qri) * 1000;
}

#endif /* _NET_AMT_H_ */
Loading