summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2016-09-03 13:46:58 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2016-09-03 13:46:58 +0000
commitf25fb3ff1b4179dce1d40d4efd3eb083850caccf (patch)
treecef66f3c3c2d3be0f639eab68f35d4bd0998cb59
parent2d375858bb375fb1b514a87b235a60a5151cac17 (diff)
Add support for a multipoint-to-multipoint mode in vxlan(4). In this
mode, vxlan(4) must be configured to accept any virtual network identifier with "vnetid any" and added to a bridge(4) or switch(4). This way the driver will dynamically learn the tunnel endpoints and their vnetids for the responses and can be used to dynamically bridge between VXLANs. It is also being used in combination with switch(4) and the OpenFlow tunnel classifiers. With input from yasuoka@ goda@ OK deraadt@ dlg@
-rw-r--r--sbin/ifconfig/ifconfig.89
-rw-r--r--sbin/ifconfig/ifconfig.c25
-rw-r--r--share/man/man4/vxlan.420
-rw-r--r--sys/net/bridgectl.c19
-rw-r--r--sys/net/if.h4
-rw-r--r--sys/net/if_bridge.c70
-rw-r--r--sys/net/if_bridge.h22
-rw-r--r--sys/net/if_switch.c41
-rw-r--r--sys/net/if_vlan.c15
-rw-r--r--sys/net/if_vxlan.c96
-rw-r--r--sys/net/if_vxlan.h8
-rw-r--r--sys/netinet/udp_usrreq.c22
12 files changed, 253 insertions, 98 deletions
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index d3cff744311..9dce28326d1 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -1,4 +1,4 @@
-.\" $OpenBSD: ifconfig.8,v 1.270 2016/06/15 19:39:33 gerhard Exp $
+.\" $OpenBSD: ifconfig.8,v 1.271 2016/09/03 13:46:57 reyk Exp $
.\" $NetBSD: ifconfig.8,v 1.11 1996/01/04 21:27:29 pk Exp $
.\" $FreeBSD: ifconfig.8,v 1.16 1998/02/01 07:03:29 steve Exp $
.\"
@@ -31,7 +31,7 @@
.\"
.\" @(#)ifconfig.8 8.4 (Berkeley) 6/1/94
.\"
-.Dd $Mdocdate: June 15 2016 $
+.Dd $Mdocdate: September 3 2016 $
.Dt IFCONFIG 8
.Os
.Sh NAME
@@ -1603,6 +1603,11 @@ to identify packets with a virtual network.
The accepted size of the number depends on the individual tunnel protocol;
it is a 24-bit number for
.Xr vxlan 4 .
+If supported by the tunnel protocol,
+the value can also be set to
+.Ar any
+to accept packets with arbitrary network identifiers (eg. for
+multipoint-to-multipoint modes).
.It Cm -vnetid
Clear the virtual network identifier.
.El
diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c
index 7b53792682c..f213d21d18c 100644
--- a/sbin/ifconfig/ifconfig.c
+++ b/sbin/ifconfig/ifconfig.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ifconfig.c,v 1.329 2016/09/02 10:01:36 goda Exp $ */
+/* $OpenBSD: ifconfig.c,v 1.330 2016/09/03 13:46:57 reyk Exp $ */
/* $NetBSD: ifconfig.c,v 1.40 1997/10/01 02:19:43 enami Exp $ */
/*
@@ -99,6 +99,7 @@
#include <err.h>
#include <errno.h>
#include <stdio.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -3622,13 +3623,18 @@ void
setvnetid(const char *id, int param)
{
const char *errmsg = NULL;
- uint32_t vnetid;
-
- vnetid = strtonum(id, 0, UINT_MAX, &errmsg);
- if (errmsg)
- errx(1, "vnetid %s: %s", id, errmsg);
+ int64_t vnetid;
strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+ if (strcasecmp("any", id) == 0)
+ vnetid = -1;
+ else {
+ vnetid = strtonum(id, 0, INT64_MAX, &errmsg);
+ if (errmsg)
+ errx(1, "vnetid %s: %s", id, errmsg);
+ }
+
ifr.ifr_vnetid = vnetid;
if (ioctl(s, SIOCSVNETID, (caddr_t)&ifr) < 0)
warn("SIOCSVNETID");
@@ -3658,7 +3664,12 @@ getvnetid(void)
return;
}
- printf("\tvnetid: %u\n", ifr.ifr_vnetid);
+ if (ifr.ifr_vnetid < 0) {
+ printf("\tvnetid: any\n");
+ return;
+ }
+
+ printf("\tvnetid: %lld\n", ifr.ifr_vnetid);
}
void
diff --git a/share/man/man4/vxlan.4 b/share/man/man4/vxlan.4
index d4cf114d39b..d6e36cc9faf 100644
--- a/share/man/man4/vxlan.4
+++ b/share/man/man4/vxlan.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: vxlan.4,v 1.4 2015/10/02 15:26:55 sobrado Exp $
+.\" $OpenBSD: vxlan.4,v 1.5 2016/09/03 13:46:57 reyk Exp $
.\"
.\" Copyright (c) 2013 Reyk Floeter <reyk@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: October 2 2015 $
+.Dd $Mdocdate: September 3 2016 $
.Dt VXLAN 4
.Os
.Sh NAME
@@ -61,7 +61,21 @@ is configured for multicast mode and added to a
.Xr bridge 4 ,
all broadcast and multicast traffic is sent to the multicast group,
but directed traffic is sent to unicast IP addresses of individual tunnel
-endpoints, as they are learned by the bridge.
+endpoints as they are learned by the bridge.
+.It Ic multipoint mode
+When
+.Nm
+is configured with
+.Ic vnetid any
+and added to a
+.Xr bridge 4 ,
+all return traffic is sent to the known tunnel endpoints
+as they are learned by the bridge.
+In this mode,
+.Nm
+does not use a specific virtual network identifier but learns the ones
+of the individual tunnel endpoints.
+It is used to dynamically bridge many virtual networks together.
.El
.Pp
The configuration can be done at runtime or by setting up a
diff --git a/sys/net/bridgectl.c b/sys/net/bridgectl.c
index 93c638c748e..4e306927bb4 100644
--- a/sys/net/bridgectl.c
+++ b/sys/net/bridgectl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: bridgectl.c,v 1.2 2015/12/02 08:04:12 mpi Exp $ */
+/* $OpenBSD: bridgectl.c,v 1.3 2016/09/03 13:46:57 reyk Exp $ */
/*
* Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
@@ -175,13 +175,13 @@ bridge_rtupdate(struct bridge_softc *sc, struct ether_addr *ea,
struct ifnet *ifp, int setflags, u_int8_t flags, struct mbuf *m)
{
struct bridge_rtnode *p, *q;
- struct sockaddr *sa = NULL;
+ struct bridge_tunneltag *brtag = NULL;
u_int32_t h;
int dir;
if (m != NULL) {
/* Check if the mbuf was tagged with a tunnel endpoint addr */
- sa = bridge_tunnel(m);
+ brtag = bridge_tunnel(m);
}
h = bridge_hash(sc, ea);
@@ -196,7 +196,7 @@ bridge_rtupdate(struct bridge_softc *sc, struct ether_addr *ea,
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
p->brt_if = ifp;
p->brt_age = 1;
- bridge_copyaddr(sa, (struct sockaddr *)&p->brt_tunnel);
+ bridge_copytag(brtag, &p->brt_tunnel);
if (setflags)
p->brt_flags = flags;
@@ -223,8 +223,7 @@ bridge_rtupdate(struct bridge_softc *sc, struct ether_addr *ea,
if (q->brt_if == ifp)
q->brt_age = 1;
ifp = q->brt_if;
- bridge_copyaddr(sa,
- (struct sockaddr *)&q->brt_tunnel);
+ bridge_copytag(brtag, &q->brt_tunnel);
goto want;
}
@@ -239,8 +238,7 @@ bridge_rtupdate(struct bridge_softc *sc, struct ether_addr *ea,
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
p->brt_if = ifp;
p->brt_age = 1;
- bridge_copyaddr(sa,
- (struct sockaddr *)&p->brt_tunnel);
+ bridge_copytag(brtag, &p->brt_tunnel);
if (setflags)
p->brt_flags = flags;
@@ -262,8 +260,7 @@ bridge_rtupdate(struct bridge_softc *sc, struct ether_addr *ea,
bcopy(ea, &p->brt_addr, sizeof(p->brt_addr));
p->brt_if = ifp;
p->brt_age = 1;
- bridge_copyaddr(sa,
- (struct sockaddr *)&p->brt_tunnel);
+ bridge_copytag(brtag, &p->brt_tunnel);
if (setflags)
p->brt_flags = flags;
@@ -490,7 +487,7 @@ bridge_rtfind(struct bridge_softc *sc, struct ifbaconf *baconf)
sizeof(bareq.ifba_ifsname));
bcopy(&n->brt_addr, &bareq.ifba_dst,
sizeof(bareq.ifba_dst));
- bridge_copyaddr(&n->brt_tunnel.sa,
+ bridge_copyaddr(&n->brt_tunnel.brtag_src.sa,
(struct sockaddr *)&bareq.ifba_dstsa);
bareq.ifba_age = n->brt_age;
bareq.ifba_flags = n->brt_flags;
diff --git a/sys/net/if.h b/sys/net/if.h
index 3022b38482b..29d1cb074d9 100644
--- a/sys/net/if.h
+++ b/sys/net/if.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if.h,v 1.177 2016/06/10 20:33:29 vgross Exp $ */
+/* $OpenBSD: if.h,v 1.178 2016/09/03 13:46:57 reyk Exp $ */
/* $NetBSD: if.h,v 1.23 1996/05/07 02:40:27 thorpej Exp $ */
/*
@@ -360,7 +360,7 @@ struct ifreq {
struct sockaddr ifru_broadaddr;
short ifru_flags;
int ifru_metric;
- uint32_t ifru_vnetid;
+ int64_t ifru_vnetid;
uint64_t ifru_media;
caddr_t ifru_data;
unsigned int ifru_index;
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index 80c4b7e67ec..1b01b47c295 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_bridge.c,v 1.283 2016/09/02 10:01:36 goda Exp $ */
+/* $OpenBSD: if_bridge.c,v 1.284 2016/09/03 13:46:57 reyk Exp $ */
/*
* Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
@@ -715,6 +715,7 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
struct bridge_rtnode *dst_p = NULL;
struct ether_addr *dst;
struct bridge_softc *sc;
+ struct bridge_tunneltag *brtag;
int error;
/* ifp must be a member interface of the bridge. */
@@ -811,9 +812,15 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
}
sendunicast:
- if (dst_p != NULL && dst_p->brt_tunnel.sa.sa_family != AF_UNSPEC &&
- (sa = bridge_tunneltag(m, dst_p->brt_tunnel.sa.sa_family)) != NULL)
- memcpy(sa, &dst_p->brt_tunnel.sa, dst_p->brt_tunnel.sa.sa_len);
+ if ((dst_p != NULL) &&
+ (dst_p->brt_tunnel.brtag_dst.sa.sa_family != AF_UNSPEC) &&
+ ((brtag = bridge_tunneltag(m)) != NULL)) {
+ memcpy(&brtag->brtag_src, &dst_p->brt_tunnel.brtag_src.sa,
+ dst_p->brt_tunnel.brtag_src.sa.sa_len);
+ memcpy(&brtag->brtag_dst, &dst_p->brt_tunnel.brtag_dst.sa,
+ dst_p->brt_tunnel.brtag_dst.sa.sa_len);
+ brtag->brtag_id = dst_p->brt_tunnel.brtag_id;
+ }
bridge_span(sc, m);
if ((dst_if->if_flags & IFF_RUNNING) == 0) {
@@ -1942,7 +1949,7 @@ bridge_send_icmp_err(struct bridge_softc *sc, struct ifnet *ifp,
m_freem(n);
}
-struct sockaddr *
+struct bridge_tunneltag *
bridge_tunnel(struct mbuf *m)
{
struct m_tag *mtag;
@@ -1950,41 +1957,24 @@ bridge_tunnel(struct mbuf *m)
if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) == NULL)
return (NULL);
- return ((struct sockaddr *)(mtag + 1));
+ return ((struct bridge_tunneltag *)(mtag + 1));
}
-struct sockaddr *
-bridge_tunneltag(struct mbuf *m, int af)
+struct bridge_tunneltag *
+bridge_tunneltag(struct mbuf *m)
{
- struct m_tag *mtag;
- size_t len;
- struct sockaddr *sa;
-
- if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) != NULL) {
- sa = (struct sockaddr *)(mtag + 1);
- if (sa->sa_family != af) {
- m_tag_delete(m, mtag);
- mtag = NULL;
- }
- }
- if (mtag == NULL) {
- if (af == AF_INET)
- len = sizeof(struct sockaddr_in);
- else if (af == AF_INET6)
- len = sizeof(struct sockaddr_in6);
- else
- return (NULL);
- mtag = m_tag_get(PACKET_TAG_TUNNEL, len, M_NOWAIT);
+ struct m_tag *mtag;
+
+ if ((mtag = m_tag_find(m, PACKET_TAG_TUNNEL, NULL)) == NULL) {
+ mtag = m_tag_get(PACKET_TAG_TUNNEL,
+ sizeof(struct bridge_tunneltag), M_NOWAIT);
if (mtag == NULL)
return (NULL);
- bzero(mtag + 1, len);
- sa = (struct sockaddr *)(mtag + 1);
- sa->sa_family = af;
- sa->sa_len = len;
+ bzero(mtag + 1, sizeof(struct bridge_tunneltag));
m_tag_prepend(m, mtag);
}
- return ((struct sockaddr *)(mtag + 1));
+ return ((struct bridge_tunneltag *)(mtag + 1));
}
void
@@ -2000,6 +1990,20 @@ bridge_copyaddr(struct sockaddr *src, struct sockaddr *dst)
{
if (src != NULL && src->sa_family != AF_UNSPEC)
memcpy(dst, src, src->sa_len);
- else
+ else {
dst->sa_family = AF_UNSPEC;
+ dst->sa_len = 0;
+ }
+}
+
+void
+bridge_copytag(struct bridge_tunneltag *src, struct bridge_tunneltag *dst)
+{
+ if (src == NULL) {
+ memset(dst, 0, sizeof(*dst));
+ } else {
+ bridge_copyaddr(&src->brtag_src.sa, &dst->brtag_src.sa);
+ bridge_copyaddr(&src->brtag_dst.sa, &dst->brtag_dst.sa);
+ dst->brtag_id = src->brtag_id;
+ }
}
diff --git a/sys/net/if_bridge.h b/sys/net/if_bridge.h
index df1a3b9f534..95a5144c0ca 100644
--- a/sys/net/if_bridge.h
+++ b/sys/net/if_bridge.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_bridge.h,v 1.50 2016/09/02 10:01:36 goda Exp $ */
+/* $OpenBSD: if_bridge.h,v 1.51 2016/09/03 13:46:57 reyk Exp $ */
/*
* Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
@@ -406,15 +406,24 @@ struct bridge_iflist {
((struct bridge_iflist *)_bp2)->bridge_sc)
/*
+ * Bridge tunnel tagging
+ */
+struct bridge_tunneltag {
+ union pfsockaddr_union brtag_src;
+ union pfsockaddr_union brtag_dst;
+ u_int32_t brtag_id;
+};
+
+/*
* Bridge route node
*/
struct bridge_rtnode {
LIST_ENTRY(bridge_rtnode) brt_next; /* next in list */
- struct ifnet *brt_if; /* destination ifs */
+ struct ifnet *brt_if; /* destination ifs */
u_int8_t brt_flags; /* address flags */
u_int8_t brt_age; /* age counter */
- struct ether_addr brt_addr; /* dst addr */
- union pfsockaddr_union brt_tunnel; /* tunnel endpoint */
+ struct ether_addr brt_addr; /* dst addr */
+ struct bridge_tunneltag brt_tunnel; /* tunnel endpoint */
};
#ifndef BRIDGE_RTABLE_SIZE
@@ -447,10 +456,11 @@ int bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *,
void bridge_update(struct ifnet *, struct ether_addr *, int);
void bridge_rtdelete(struct bridge_softc *, struct ifnet *, int);
void bridge_rtagenode(struct ifnet *, int);
-struct sockaddr *bridge_tunnel(struct mbuf *);
-struct sockaddr *bridge_tunneltag(struct mbuf *, int);
+struct bridge_tunneltag *bridge_tunnel(struct mbuf *);
+struct bridge_tunneltag *bridge_tunneltag(struct mbuf *);
void bridge_tunneluntag(struct mbuf *);
void bridge_copyaddr(struct sockaddr *, struct sockaddr *);
+void bridge_copytag(struct bridge_tunneltag *, struct bridge_tunneltag *);
struct bstp_state *bstp_create(struct ifnet *);
void bstp_destroy(struct bstp_state *);
diff --git a/sys/net/if_switch.c b/sys/net/if_switch.c
index 8f481583981..c0032f93749 100644
--- a/sys/net/if_switch.c
+++ b/sys/net/if_switch.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_switch.c,v 1.2 2016/09/02 10:01:36 goda Exp $ */
+/* $OpenBSD: if_switch.c,v 1.3 2016/09/03 13:46:57 reyk Exp $ */
/*
* Copyright (c) 2016 Kazuya GODA <goda@openbsd.org>
@@ -1376,6 +1376,43 @@ switch_flow_classifier_ether(struct mbuf *m, int *offset,
}
struct mbuf *
+switch_flow_classifier_tunnel(struct mbuf *m, int *offset,
+ struct switch_flow_classify *swfcl)
+{
+ struct bridge_tunneltag *brtag;
+
+ if ((brtag = bridge_tunnel(m)) == NULL)
+ goto out;
+
+ if ((brtag->brtag_dst.sa.sa_family != AF_INET) &&
+ (brtag->brtag_dst.sa.sa_family != AF_INET6))
+ goto out;
+
+ swfcl->swfcl_tunnel = pool_get(&swfcl_pool, PR_NOWAIT|PR_ZERO);
+ if (swfcl->swfcl_tunnel == NULL) {
+ m_freem(m);
+ return (NULL);
+ }
+
+ swfcl->swfcl_tunnel->tun_af = brtag->brtag_dst.sa.sa_family;
+ swfcl->swfcl_tunnel->tun_key = htobe64(brtag->brtag_id);
+ if (swfcl->swfcl_tunnel->tun_af == AF_INET) {
+ swfcl->swfcl_tunnel->tun_ipv4_src =
+ brtag->brtag_src.sin.sin_addr;
+ swfcl->swfcl_tunnel->tun_ipv4_dst =
+ brtag->brtag_dst.sin.sin_addr;
+ } else {
+ swfcl->swfcl_tunnel->tun_ipv6_src =
+ brtag->brtag_src.sin6.sin6_addr;
+ swfcl->swfcl_tunnel->tun_ipv6_dst =
+ brtag->brtag_dst.sin6.sin6_addr;
+ }
+ bridge_tunneluntag(m);
+ out:
+ return switch_flow_classifier_ether(m, offset, swfcl);
+}
+
+struct mbuf *
switch_flow_classifier(struct mbuf *m, uint32_t in_port,
struct switch_flow_classify *swfcl)
{
@@ -1384,7 +1421,7 @@ switch_flow_classifier(struct mbuf *m, uint32_t in_port,
memset(swfcl, 0, sizeof(*swfcl));
swfcl->swfcl_in_port = in_port;
- return switch_flow_classifier_ether(m, &offset, swfcl);
+ return switch_flow_classifier_tunnel(m, &offset, swfcl);
}
void
diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c
index 0aa47d77fb0..7b23bf8dee8 100644
--- a/sys/net/if_vlan.c
+++ b/sys/net/if_vlan.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_vlan.c,v 1.165 2016/05/18 03:46:03 dlg Exp $ */
+/* $OpenBSD: if_vlan.c,v 1.166 2016/09/03 13:46:57 reyk Exp $ */
/*
* Copyright 1998 Massachusetts Institute of Technology
@@ -648,15 +648,16 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
case SIOCSVNETID:
- tag = ifr->ifr_vnetid;
- if (tag == ifv->ifv_tag)
- break;
-
- if (tag < EVL_VLID_MIN || tag > EVL_VLID_MAX) {
+ if (ifr->ifr_vnetid < EVL_VLID_MIN ||
+ ifr->ifr_vnetid > EVL_VLID_MAX) {
error = EINVAL;
break;
}
+ tag = ifr->ifr_vnetid;
+ if (tag == ifv->ifv_tag)
+ break;
+
error = vlan_set_vnetid(ifv, tag);
break;
@@ -664,7 +665,7 @@ vlan_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
if (ifv->ifv_tag == EVL_VLID_NULL)
error = EADDRNOTAVAIL;
else
- ifr->ifr_vnetid = (uint32_t)ifv->ifv_tag;
+ ifr->ifr_vnetid = (int64_t)ifv->ifv_tag;
break;
case SIOCDVNETID:
diff --git a/sys/net/if_vxlan.c b/sys/net/if_vxlan.c
index 3cd59a79b5b..4050030d9ad 100644
--- a/sys/net/if_vxlan.c
+++ b/sys/net/if_vxlan.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_vxlan.c,v 1.42 2016/08/07 14:26:26 reyk Exp $ */
+/* $OpenBSD: if_vxlan.c,v 1.43 2016/09/03 13:46:57 reyk Exp $ */
/*
* Copyright (c) 2013 Reyk Floeter <reyk@openbsd.org>
@@ -70,7 +70,7 @@ struct vxlan_softc {
struct sockaddr_storage sc_dst;
in_port_t sc_dstport;
u_int sc_rdomain;
- int sc_vnetid;
+ int64_t sc_vnetid;
u_int8_t sc_ttl;
LIST_ENTRY(vxlan_softc) sc_entry;
@@ -103,15 +103,19 @@ u_long vxlan_tagmask;
#define VXLAN_TAGHASHSIZE 32
#define VXLAN_TAGHASH(tag) ((unsigned int)tag & vxlan_tagmask)
-LIST_HEAD(vxlan_taghash, vxlan_softc) *vxlan_tagh;
+LIST_HEAD(vxlan_taghash, vxlan_softc) *vxlan_tagh, vxlan_any;
void
vxlanattach(int count)
{
+ /* Regular vxlan interfaces with a VNI */
if ((vxlan_tagh = hashinit(VXLAN_TAGHASHSIZE, M_DEVBUF, M_NOWAIT,
&vxlan_tagmask)) == NULL)
panic("vxlanattach: hashinit");
+ /* multipoint-to-multipoint interfaces that accept any VNI */
+ LIST_INIT(&vxlan_any);
+
if_clone_attach(&vxlan_cloner);
}
@@ -316,6 +320,7 @@ vxlan_config(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst)
int reset = 0, error, af;
socklen_t slen;
in_port_t port;
+ struct vxlan_taghash *tagh;
if (src != NULL && dst != NULL) {
if ((af = src->sa_family) != dst->sa_family)
@@ -354,9 +359,17 @@ vxlan_config(struct ifnet *ifp, struct sockaddr *src, struct sockaddr *dst)
memcpy(&sc->sc_dst, dst, dst->sa_len);
}
+ if (sc->sc_vnetid == VXLAN_VNI_ANY) {
+ /*
+ * If the interface accepts any VNI, put it into a separate
+ * list that is not part of the main hash.
+ */
+ tagh = &vxlan_any;
+ } else
+ tagh = &vxlan_tagh[VXLAN_TAGHASH(sc->sc_vnetid)];
+
LIST_REMOVE(sc, sc_entry);
- LIST_INSERT_HEAD(&vxlan_tagh[VXLAN_TAGHASH(sc->sc_vnetid)],
- sc, sc_entry);
+ LIST_INSERT_HEAD(tagh, sc, sc_entry);
return (0);
}
@@ -454,10 +467,16 @@ vxlanioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
case SIOCSVNETID:
- if (ifr->ifr_vnetid > VXLAN_VNI_MAX) {
+ if (sc->sc_vnetid == ifr->ifr_vnetid)
+ break;
+
+ if ((ifr->ifr_vnetid != VXLAN_VNI_ANY) &&
+ (ifr->ifr_vnetid > VXLAN_VNI_MAX ||
+ ifr->ifr_vnetid < VXLAN_VNI_MIN)) {
error = EINVAL;
break;
}
+
s = splnet();
sc->sc_vnetid = (int)ifr->ifr_vnetid;
(void)vxlan_config(ifp, NULL, NULL);
@@ -465,11 +484,14 @@ vxlanioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
break;
case SIOCGVNETID:
- if (sc->sc_vnetid == VXLAN_VNI_UNSET) {
+ if ((sc->sc_vnetid != VXLAN_VNI_ANY) &&
+ (sc->sc_vnetid > VXLAN_VNI_MAX ||
+ sc->sc_vnetid < VXLAN_VNI_MIN)) {
error = EADDRNOTAVAIL;
break;
}
- ifr->ifr_vnetid = (uint32_t)sc->sc_vnetid;
+
+ ifr->ifr_vnetid = sc->sc_vnetid;
break;
case SIOCDVNETID:
@@ -496,7 +518,6 @@ vxlan_media_change(struct ifnet *ifp)
void
vxlan_media_status(struct ifnet *ifp, struct ifmediareq *imr)
{
- imr->ifm_active = IFM_ETHER | IFM_AUTO;
imr->ifm_status = IFM_AVALID | IFM_ACTIVE;
}
@@ -547,7 +568,7 @@ vxlan_sockaddr_port(struct sockaddr *sa)
int
vxlan_lookup(struct mbuf *m, struct udphdr *uh, int iphlen,
- struct sockaddr *srcsa)
+ struct sockaddr *srcsa, struct sockaddr *dstsa)
{
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
struct vxlan_softc *sc = NULL, *sc_cand = NULL;
@@ -557,7 +578,7 @@ vxlan_lookup(struct mbuf *m, struct udphdr *uh, int iphlen,
int skip;
struct ether_header *eh;
#if NBRIDGE > 0
- struct sockaddr *sa;
+ struct bridge_tunneltag *brtag;
#endif
/* XXX Should verify the UDP port first before copying the packet */
@@ -579,6 +600,7 @@ vxlan_lookup(struct mbuf *m, struct udphdr *uh, int iphlen,
vni = VXLAN_VNI_UNSET;
}
+ /* First search for a vxlan(4) interface with the packet's VNI */
LIST_FOREACH(sc, &vxlan_tagh[VXLAN_TAGHASH(vni)], sc_entry) {
if ((uh->uh_dport == sc->sc_dstport) &&
vni == sc->sc_vnetid &&
@@ -590,6 +612,21 @@ vxlan_lookup(struct mbuf *m, struct udphdr *uh, int iphlen,
}
}
+ /*
+ * Now loop through all the vxlan(4) interfaces that are configured
+ * to accept any VNI and operating in multipoint-to-multipoint mode
+ * that is used in combination with bridge(4) or switch(4).
+ * If a vxlan(4) interface has been found for the packet's VNI, this
+ * code is not reached as the other interface is more specific.
+ */
+ LIST_FOREACH(sc, &vxlan_any, sc_entry) {
+ if ((uh->uh_dport == sc->sc_dstport) &&
+ (sc->sc_rdomain == rtable_l2(m->m_pkthdr.ph_rtableid))) {
+ sc_cand = sc;
+ goto found;
+ }
+ }
+
if (sc_cand) {
sc = sc_cand;
goto found;
@@ -606,11 +643,14 @@ vxlan_lookup(struct mbuf *m, struct udphdr *uh, int iphlen,
return (EINVAL);
#if NBRIDGE > 0
- /* Store the peer IP address for the bridge */
- if (ifp->if_bridgeport != NULL &&
+ /* Store the tunnel src/dst IP and vni for the bridge or switch */
+ if ((ifp->if_bridgeport != NULL || ifp->if_switchport != NULL) &&
srcsa->sa_family != AF_UNSPEC &&
- (sa = bridge_tunneltag(m, srcsa->sa_family)) != NULL)
- memcpy(sa, srcsa, sa->sa_len);
+ ((brtag = bridge_tunneltag(m)) != NULL)) {
+ memcpy(&brtag->brtag_src.sa, srcsa, srcsa->sa_len);
+ memcpy(&brtag->brtag_dst.sa, dstsa, dstsa->sa_len);
+ brtag->brtag_id = vni;
+ }
#endif
m->m_flags &= ~(M_MCAST|M_BCAST);
@@ -712,9 +752,10 @@ vxlan_output(struct ifnet *ifp, struct mbuf *m)
struct vxlanudphdr *vu;
struct sockaddr *src, *dst;
#if NBRIDGE > 0
- struct sockaddr *brtag;
+ struct bridge_tunneltag *brtag;
#endif
int error, af;
+ uint32_t tag;
/* VXLAN header */
M_PREPEND(m, sizeof(*vu), M_DONTWAIT);
@@ -732,10 +773,18 @@ vxlan_output(struct ifnet *ifp, struct mbuf *m)
vu->vu_u.uh_dport = sc->sc_dstport;
vu->vu_u.uh_ulen = htons(m->m_pkthdr.len);
vu->vu_u.uh_sum = 0;
+ tag = sc->sc_vnetid;
#if NBRIDGE > 0
if ((brtag = bridge_tunnel(m)) != NULL) {
- dst = brtag;
+ dst = &brtag->brtag_dst.sa;
+
+ /* If accepting any VNI, source ip address is from brtag */
+ if (sc->sc_vnetid == VXLAN_VNI_ANY) {
+ src = &brtag->brtag_src.sa;
+ tag = (uint32_t)brtag->brtag_id;
+ af = src->sa_family;
+ }
if (dst->sa_family != af) {
ifp->if_oerrors++;
@@ -750,12 +799,21 @@ vxlan_output(struct ifnet *ifp, struct mbuf *m)
*/
if (ifp->if_flags & IFF_LINK0)
vu->vu_u.uh_dport = vxlan_sockaddr_port(dst);
- }
+ } else
#endif
+ if (sc->sc_vnetid == VXLAN_VNI_ANY) {
+ /*
+ * If accepting any VNI, build the vxlan header only by
+ * bridge_tunneltag or drop packet if the tag does not exist.
+ */
+ ifp->if_oerrors++;
+ m_freem(m);
+ return (ENETUNREACH);
+ }
if (sc->sc_vnetid != VXLAN_VNI_UNSET) {
vu->vu_v.vxlan_flags = htonl(VXLAN_FLAGS_VNI);
- vu->vu_v.vxlan_id = htonl(sc->sc_vnetid << VXLAN_VNI_S);
+ vu->vu_v.vxlan_id = htonl(tag << VXLAN_VNI_S);
} else {
vu->vu_v.vxlan_flags = htonl(0);
vu->vu_v.vxlan_id = htonl(0);
diff --git a/sys/net/if_vxlan.h b/sys/net/if_vxlan.h
index c6ed25b8d66..33968772e4d 100644
--- a/sys/net/if_vxlan.h
+++ b/sys/net/if_vxlan.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_vxlan.h,v 1.10 2016/08/07 13:49:12 reyk Exp $ */
+/* $OpenBSD: if_vxlan.h,v 1.11 2016/09/03 13:46:57 reyk Exp $ */
/*
* Copyright (c) 2013 Reyk Floeter <reyk@openbsd.org>
@@ -34,7 +34,9 @@ struct vxlan_header {
} __packed;
#define VXLAN_VNI_MAX 0x00ffffff /* 24bit vnetid */
-#define VXLAN_VNI_UNSET 0xffffffff /* -1 */
+#define VXLAN_VNI_MIN 0x00000000 /* 24bit vnetid */
+#define VXLAN_VNI_UNSET 0x01ffffff /* used internally */
+#define VXLAN_VNI_ANY -1ULL /* -1 accept any vnetid */
struct vxlanudphdr {
struct udphdr vu_u;
@@ -45,7 +47,7 @@ struct vxlanudphdr {
extern int vxlan_enable;
int vxlan_lookup(struct mbuf *, struct udphdr *, int,
- struct sockaddr *);
+ struct sockaddr *, struct sockaddr *);
struct sockaddr *vxlan_tag_find(struct mbuf *);
struct sockaddr *vxlan_tag_get(struct mbuf *, int);
void vxlan_tag_delete(struct mbuf *);
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index 01b6c5b64ef..60a7bdf41bd 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: udp_usrreq.c,v 1.218 2016/08/16 22:21:17 vgross Exp $ */
+/* $OpenBSD: udp_usrreq.c,v 1.219 2016/09/03 13:46:57 reyk Exp $ */
/* $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */
/*
@@ -173,7 +173,7 @@ udp_input(struct mbuf *m, ...)
#ifdef INET6
struct sockaddr_in6 sin6;
#endif /* INET6 */
- } srcsa;
+ } srcsa, dstsa;
#ifdef INET6
struct ip6_hdr *ip6;
#endif /* INET6 */
@@ -353,6 +353,12 @@ udp_input(struct mbuf *m, ...)
srcsa.sin.sin_family = AF_INET;
srcsa.sin.sin_port = uh->uh_sport;
srcsa.sin.sin_addr = ip->ip_src;
+
+ bzero(&dstsa, sizeof(struct sockaddr_in));
+ dstsa.sin.sin_len = sizeof(struct sockaddr_in);
+ dstsa.sin.sin_family = AF_INET;
+ dstsa.sin.sin_port = uh->uh_dport;
+ dstsa.sin.sin_addr = ip->ip_dst;
break;
#ifdef INET6
case AF_INET6:
@@ -365,6 +371,16 @@ udp_input(struct mbuf *m, ...)
#endif
/* KAME hack: recover scopeid */
in6_recoverscope(&srcsa.sin6, &ip6->ip6_src);
+
+ bzero(&dstsa, sizeof(struct sockaddr_in6));
+ dstsa.sin6.sin6_len = sizeof(struct sockaddr_in6);
+ dstsa.sin6.sin6_family = AF_INET6;
+ dstsa.sin6.sin6_port = uh->uh_dport;
+#if 0 /*XXX inbound flowinfo */
+ dstsa.sin6.sin6_flowinfo = htonl(0x0fffffff) & ip6->ip6_flow;
+#endif
+ /* KAME hack: recover scopeid */
+ in6_recoverscope(&dstsa.sin6, &ip6->ip6_dst);
break;
#endif /* INET6 */
}
@@ -374,7 +390,7 @@ udp_input(struct mbuf *m, ...)
#if NPF > 0
!(m->m_pkthdr.pf.flags & PF_TAG_DIVERTED) &&
#endif
- (error = vxlan_lookup(m, uh, iphlen, &srcsa.sa)) != 0) {
+ (error = vxlan_lookup(m, uh, iphlen, &srcsa.sa, &dstsa.sa)) != 0) {
if (error == -1) {
udpstat.udps_hdrops++;
m_freem(m);