2707 lines · 66.9 KB · C
RawDownload
/* * VXLAN: Virtual eXtensible Local Area Network * * Copyright (c) 2012-2013 Vyatta Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h>#include <linux/types.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/skbuff.h>#include <linux/rculist.h>#include <linux/netdevice.h>#include <linux/in.h>#include <linux/ip.h>#include <linux/udp.h>#include <linux/igmp.h>#include <linux/etherdevice.h>#include <linux/if_ether.h>#include <linux/if_vlan.h>#include <linux/hash.h>#include <linux/ethtool.h>#include <net/arp.h>#include <net/ndisc.h>#include <net/ip.h>#include <net/ip_tunnels.h>#include <net/icmp.h>#include <net/udp.h>#include <net/rtnetlink.h>#include <net/route.h>#include <net/dsfield.h>#include <net/inet_ecn.h>#include <net/net_namespace.h>#include <net/netns/generic.h>#include <net/vxlan.h>#if IS_ENABLED(CONFIG_IPV6)#include <net/ipv6.h>#include <net/addrconf.h>#include <net/ip6_tunnel.h>#include <net/ip6_checksum.h>#endif #define VXLAN_VERSION	"0.1" #define PORT_HASH_BITS	8#define PORT_HASH_SIZE  (1<<PORT_HASH_BITS)#define VNI_HASH_BITS	10#define VNI_HASH_SIZE	(1<<VNI_HASH_BITS)#define FDB_HASH_BITS	8#define FDB_HASH_SIZE	(1<<FDB_HASH_BITS)#define FDB_AGE_DEFAULT 300 /* 5 min */#define FDB_AGE_INTERVAL (10 * HZ)	/* rescan interval */ #define VXLAN_N_VID	(1u << 24)#define VXLAN_VID_MASK	(VXLAN_N_VID - 1)#define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr)) #define VXLAN_FLAGS 0x08000000	/* struct vxlanhdr.vx_flags required value. */ /* VXLAN protocol header */struct vxlanhdr {	__be32 vx_flags;	__be32 vx_vni;}; /* UDP port for VXLAN traffic. * The IANA assigned port is 4789, but the Linux default is 8472 * for compatibility with early adopters. */static unsigned short vxlan_port __read_mostly = 8472;module_param_named(udp_port, vxlan_port, ushort, 0444);MODULE_PARM_DESC(udp_port, "Destination UDP port"); static bool log_ecn_error = true;module_param(log_ecn_error, bool, 0644);MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); static int vxlan_net_id; static const u8 all_zeros_mac[ETH_ALEN]; /* per-network namespace private data for this module */struct vxlan_net {	struct list_head  vxlan_list;	struct hlist_head sock_list[PORT_HASH_SIZE];	spinlock_t	  sock_lock;}; union vxlan_addr {	struct sockaddr_in sin;	struct sockaddr_in6 sin6;	struct sockaddr sa;}; struct vxlan_rdst {	union vxlan_addr	 remote_ip;	__be16			 remote_port;	u32			 remote_vni;	u32			 remote_ifindex;	struct list_head	 list;	struct rcu_head		 rcu;}; /* Forwarding table entry */struct vxlan_fdb {	struct hlist_node hlist;	/* linked list of entries */	struct rcu_head	  rcu;	unsigned long	  updated;	/* jiffies */	unsigned long	  used;	struct list_head  remotes;	u16		  state;	/* see ndm_state */	u8		  flags;	/* see ndm_flags */	u8		  eth_addr[ETH_ALEN];}; /* Pseudo network device */struct vxlan_dev {	struct hlist_node hlist;	/* vni hash table */	struct list_head  next;		/* vxlan's per namespace list */	struct vxlan_sock *vn_sock;	/* listening socket */	struct net_device *dev;	struct vxlan_rdst default_dst;	/* default destination */	union vxlan_addr  saddr;	/* source address */	__be16		  dst_port;	__u16		  port_min;	/* source port range */	__u16		  port_max;	__u8		  tos;		/* TOS override */	__u8		  ttl;	u32		  flags;	/* VXLAN_F_* below */ 	struct work_struct sock_work;	struct work_struct igmp_join;	struct work_struct igmp_leave; 	unsigned long	  age_interval;	struct timer_list age_timer;	spinlock_t	  hash_lock;	unsigned int	  addrcnt;	unsigned int	  addrmax; 	struct hlist_head fdb_head[FDB_HASH_SIZE];}; #define VXLAN_F_LEARN	0x01#define VXLAN_F_PROXY	0x02#define VXLAN_F_RSC	0x04#define VXLAN_F_L2MISS	0x08#define VXLAN_F_L3MISS	0x10#define VXLAN_F_IPV6	0x20 /* internal flag */ /* salt for hash table */static u32 vxlan_salt __read_mostly;static struct workqueue_struct *vxlan_wq; static void vxlan_sock_work(struct work_struct *work); #if IS_ENABLED(CONFIG_IPV6)static inlinebool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b){       if (a->sa.sa_family != b->sa.sa_family)               return false;       if (a->sa.sa_family == AF_INET6)               return ipv6_addr_equal(&a->sin6.sin6_addr, &b->sin6.sin6_addr);       else               return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;} static inline bool vxlan_addr_any(const union vxlan_addr *ipa){       if (ipa->sa.sa_family == AF_INET6)               return ipv6_addr_any(&ipa->sin6.sin6_addr);       else               return ipa->sin.sin_addr.s_addr == htonl(INADDR_ANY);} static inline bool vxlan_addr_multicast(const union vxlan_addr *ipa){       if (ipa->sa.sa_family == AF_INET6)               return ipv6_addr_is_multicast(&ipa->sin6.sin6_addr);       else               return IN_MULTICAST(ntohl(ipa->sin.sin_addr.s_addr));} static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla){       if (nla_len(nla) >= sizeof(struct in6_addr)) {               nla_memcpy(&ip->sin6.sin6_addr, nla, sizeof(struct in6_addr));               ip->sa.sa_family = AF_INET6;               return 0;       } else if (nla_len(nla) >= sizeof(__be32)) {               ip->sin.sin_addr.s_addr = nla_get_be32(nla);               ip->sa.sa_family = AF_INET;               return 0;       } else {               return -EAFNOSUPPORT;       }} static int vxlan_nla_put_addr(struct sk_buff *skb, int attr,                             const union vxlan_addr *ip){       if (ip->sa.sa_family == AF_INET6)               return nla_put(skb, attr, sizeof(struct in6_addr), &ip->sin6.sin6_addr);       else               return nla_put_be32(skb, attr, ip->sin.sin_addr.s_addr);} #else /* !CONFIG_IPV6 */ static inlinebool vxlan_addr_equal(const union vxlan_addr *a, const union vxlan_addr *b){       return a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr;} static inline bool vxlan_addr_any(const union vxlan_addr *ipa){       return ipa->sin.sin_addr.s_addr == htonl(INADDR_ANY);} static inline bool vxlan_addr_multicast(const union vxlan_addr *ipa){       return IN_MULTICAST(ntohl(ipa->sin.sin_addr.s_addr));} static int vxlan_nla_get_addr(union vxlan_addr *ip, struct nlattr *nla){       if (nla_len(nla) >= sizeof(struct in6_addr)) {               return -EAFNOSUPPORT;       } else if (nla_len(nla) >= sizeof(__be32)) {               ip->sin.sin_addr.s_addr = nla_get_be32(nla);               ip->sa.sa_family = AF_INET;               return 0;       } else {               return -EAFNOSUPPORT;       }} static int vxlan_nla_put_addr(struct sk_buff *skb, int attr,                             const union vxlan_addr *ip){       return nla_put_be32(skb, attr, ip->sin.sin_addr.s_addr);}#endif /* Virtual Network hash table head */static inline struct hlist_head *vni_head(struct vxlan_sock *vs, u32 id){	return &vs->vni_list[hash_32(id, VNI_HASH_BITS)];} /* Socket hash table head */static inline struct hlist_head *vs_head(struct net *net, __be16 port){	struct vxlan_net *vn = net_generic(net, vxlan_net_id); 	return &vn->sock_list[hash_32(ntohs(port), PORT_HASH_BITS)];} /* First remote destination for a forwarding entry. * Guaranteed to be non-NULL because remotes are never deleted. */static inline struct vxlan_rdst *first_remote_rcu(struct vxlan_fdb *fdb){	return list_entry_rcu(fdb->remotes.next, struct vxlan_rdst, list);} static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb){	return list_first_entry(&fdb->remotes, struct vxlan_rdst, list);} /* Find VXLAN socket based on network namespace and UDP port */static struct vxlan_sock *vxlan_find_sock(struct net *net, __be16 port){	struct vxlan_sock *vs; 	hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) {		if (inet_sk(vs->sock->sk)->inet_sport == port)			return vs;	}	return NULL;} static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, u32 id){	struct vxlan_dev *vxlan; 	hlist_for_each_entry_rcu(vxlan, vni_head(vs, id), hlist) {		if (vxlan->default_dst.remote_vni == id)			return vxlan;	} 	return NULL;} /* Look up VNI in a per net namespace table */static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, __be16 port){	struct vxlan_sock *vs; 	vs = vxlan_find_sock(net, port);	if (!vs)		return NULL; 	return vxlan_vs_find_vni(vs, id);} /* Fill in neighbour message in skbuff. */static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan,			  const struct vxlan_fdb *fdb,			  u32 portid, u32 seq, int type, unsigned int flags,			  const struct vxlan_rdst *rdst){	unsigned long now = jiffies;	struct nda_cacheinfo ci;	struct nlmsghdr *nlh;	struct ndmsg *ndm;	bool send_ip, send_eth; 	nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags);	if (nlh == NULL)		return -EMSGSIZE; 	ndm = nlmsg_data(nlh);	memset(ndm, 0, sizeof(*ndm)); 	send_eth = send_ip = true; 	if (type == RTM_GETNEIGH) {		ndm->ndm_family	= AF_INET;		send_ip = !vxlan_addr_any(&rdst->remote_ip);		send_eth = !is_zero_ether_addr(fdb->eth_addr);	} else		ndm->ndm_family	= AF_BRIDGE;	ndm->ndm_state = fdb->state;	ndm->ndm_ifindex = vxlan->dev->ifindex;	ndm->ndm_flags = fdb->flags;	ndm->ndm_type = NDA_DST; 	if (send_eth && nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->eth_addr))		goto nla_put_failure; 	if (send_ip && vxlan_nla_put_addr(skb, NDA_DST, &rdst->remote_ip))		goto nla_put_failure; 	if (rdst->remote_port && rdst->remote_port != vxlan->dst_port &&	    nla_put_be16(skb, NDA_PORT, rdst->remote_port))		goto nla_put_failure;	if (rdst->remote_vni != vxlan->default_dst.remote_vni &&	    nla_put_u32(skb, NDA_VNI, rdst->remote_vni))		goto nla_put_failure;	if (rdst->remote_ifindex &&	    nla_put_u32(skb, NDA_IFINDEX, rdst->remote_ifindex))		goto nla_put_failure; 	ci.ndm_used	 = jiffies_to_clock_t(now - fdb->used);	ci.ndm_confirmed = 0;	ci.ndm_updated	 = jiffies_to_clock_t(now - fdb->updated);	ci.ndm_refcnt	 = 0; 	if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci))		goto nla_put_failure; 	return nlmsg_end(skb, nlh); nla_put_failure:	nlmsg_cancel(skb, nlh);	return -EMSGSIZE;} static inline size_t vxlan_nlmsg_size(void){	return NLMSG_ALIGN(sizeof(struct ndmsg))		+ nla_total_size(ETH_ALEN) /* NDA_LLADDR */		+ nla_total_size(sizeof(struct in6_addr)) /* NDA_DST */		+ nla_total_size(sizeof(__be16)) /* NDA_PORT */		+ nla_total_size(sizeof(__be32)) /* NDA_VNI */		+ nla_total_size(sizeof(__u32)) /* NDA_IFINDEX */		+ nla_total_size(sizeof(struct nda_cacheinfo));} static void vxlan_fdb_notify(struct vxlan_dev *vxlan,			     struct vxlan_fdb *fdb, int type){	struct net *net = dev_net(vxlan->dev);	struct sk_buff *skb;	int err = -ENOBUFS; 	skb = nlmsg_new(vxlan_nlmsg_size(), GFP_ATOMIC);	if (skb == NULL)		goto errout; 	err = vxlan_fdb_info(skb, vxlan, fdb, 0, 0, type, 0,			     first_remote_rtnl(fdb));	if (err < 0) {		/* -EMSGSIZE implies BUG in vxlan_nlmsg_size() */		WARN_ON(err == -EMSGSIZE);		kfree_skb(skb);		goto errout;	} 	rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);	return;errout:	if (err < 0)		rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);} static void vxlan_ip_miss(struct net_device *dev, union vxlan_addr *ipa){	struct vxlan_dev *vxlan = netdev_priv(dev);	struct vxlan_fdb f = {		.state = NUD_STALE,	};	struct vxlan_rdst remote = {		.remote_ip = *ipa, /* goes to NDA_DST */		.remote_vni = VXLAN_N_VID,	}; 	INIT_LIST_HEAD(&f.remotes);	list_add_rcu(&remote.list, &f.remotes); 	vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH);} static void vxlan_fdb_miss(struct vxlan_dev *vxlan, const u8 eth_addr[ETH_ALEN]){	struct vxlan_fdb f = {		.state = NUD_STALE,	}; 	INIT_LIST_HEAD(&f.remotes);	memcpy(f.eth_addr, eth_addr, ETH_ALEN); 	vxlan_fdb_notify(vxlan, &f, RTM_GETNEIGH);} /* Hash Ethernet address */static u32 eth_hash(const unsigned char *addr){	u64 value = get_unaligned((u64 *)addr); 	/* only want 6 bytes */#ifdef __BIG_ENDIAN	value >>= 16;#else	value <<= 16;#endif	return hash_64(value, FDB_HASH_BITS);} /* Hash chain to use given mac address */static inline struct hlist_head *vxlan_fdb_head(struct vxlan_dev *vxlan,						const u8 *mac){	return &vxlan->fdb_head[eth_hash(mac)];} /* Look up Ethernet address in forwarding table */static struct vxlan_fdb *__vxlan_find_mac(struct vxlan_dev *vxlan,					const u8 *mac) {	struct hlist_head *head = vxlan_fdb_head(vxlan, mac);	struct vxlan_fdb *f; 	hlist_for_each_entry_rcu(f, head, hlist) {		if (ether_addr_equal(mac, f->eth_addr))			return f;	} 	return NULL;} static struct vxlan_fdb *vxlan_find_mac(struct vxlan_dev *vxlan,					const u8 *mac){	struct vxlan_fdb *f; 	f = __vxlan_find_mac(vxlan, mac);	if (f)		f->used = jiffies; 	return f;} /* caller should hold vxlan->hash_lock */static struct vxlan_rdst *vxlan_fdb_find_rdst(struct vxlan_fdb *f,					      union vxlan_addr *ip, __be16 port,					      __u32 vni, __u32 ifindex){

Lines 1–500 of 2707.