summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael Zalamena <rzalamena@cvs.openbsd.org>2016-11-22 17:21:57 +0000
committerRafael Zalamena <rzalamena@cvs.openbsd.org>2016-11-22 17:21:57 +0000
commitfe4ea9bd743167611749f63880194860c1abf9f0 (patch)
tree0a93a056743b4b1c13e464392a55424493f92e11
parent3845fac762e7575ef4baa76c5e2f541859654c66 (diff)
Implement support for version negotiation using hello messages. This
also prevents connections from switching the version in the middle of the operation. tweak from and ok reyk@
-rw-r--r--usr.sbin/switchd/ofp.c13
-rw-r--r--usr.sbin/switchd/ofp10.c11
-rw-r--r--usr.sbin/switchd/ofp13.c11
-rw-r--r--usr.sbin/switchd/ofp_common.c174
-rw-r--r--usr.sbin/switchd/switchd.h10
5 files changed, 200 insertions, 19 deletions
diff --git a/usr.sbin/switchd/ofp.c b/usr.sbin/switchd/ofp.c
index 7b8fadbd82a..02fd26ab16e 100644
--- a/usr.sbin/switchd/ofp.c
+++ b/usr.sbin/switchd/ofp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ofp.c,v 1.15 2016/11/04 22:27:08 reyk Exp $ */
+/* $OpenBSD: ofp.c,v 1.16 2016/11/22 17:21:56 rzalamena Exp $ */
/*
* Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org>
@@ -132,6 +132,13 @@ ofp_input(struct switch_connection *con, struct ibuf *ibuf)
return (-1);
}
+ if (con->con_version != OFP_V_0 &&
+ oh->oh_version != con->con_version) {
+ log_debug("wrong version %d, expected %d",
+ oh->oh_version, con->con_version);
+ return (-1);
+ }
+
switch (oh->oh_version) {
case OFP_V_1_0:
if (ofp10_input(sc, con, oh, ibuf) != 0)
@@ -166,6 +173,10 @@ ofp_open(struct privsep *ps, struct switch_connection *con)
__func__, con->con_id, con->con_instance,
sw == NULL ? 0 : sw->sw_id);
+ /* Send the hello with the latest version we support. */
+ if (ofp_send_hello(ps->ps_env, con, OFP_V_1_3) == -1)
+ return (-1);
+
return (0);
}
diff --git a/usr.sbin/switchd/ofp10.c b/usr.sbin/switchd/ofp10.c
index 56b7dfdee25..c990442f596 100644
--- a/usr.sbin/switchd/ofp10.c
+++ b/usr.sbin/switchd/ofp10.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ofp10.c,v 1.16 2016/11/21 18:19:51 rzalamena Exp $ */
+/* $OpenBSD: ofp10.c,v 1.17 2016/11/22 17:21:56 rzalamena Exp $ */
/*
* Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org>
@@ -60,7 +60,7 @@ int ofp10_validate_packet_out(struct switchd *,
struct ofp_header *, struct ibuf *);
struct ofp_callback ofp10_callbacks[] = {
- { OFP10_T_HELLO, ofp10_hello, NULL },
+ { OFP10_T_HELLO, ofp10_hello, ofp_validate_hello },
{ OFP10_T_ERROR, NULL, ofp10_validate_error },
{ OFP10_T_ECHO_REQUEST, ofp10_echo_request, NULL },
{ OFP10_T_ECHO_REPLY, NULL, NULL },
@@ -262,13 +262,8 @@ ofp10_hello(struct switchd *sc, struct switch_connection *con,
return (-1);
}
- /* Echo back the received Hello packet */
- oh->oh_version = OFP_V_1_0;
- oh->oh_length = htons(sizeof(*oh));
- oh->oh_xid = htonl(con->con_xidnxt++);
- if (ofp10_validate(sc, &con->con_local, &con->con_peer, oh, NULL) != 0)
+ if (ofp_recv_hello(sc, con, oh, ibuf) == -1)
return (-1);
- ofp_output(con, oh, NULL);
#if 0
(void)write(fd, &oh, sizeof(oh));
diff --git a/usr.sbin/switchd/ofp13.c b/usr.sbin/switchd/ofp13.c
index 38adacb258b..43ed45f1640 100644
--- a/usr.sbin/switchd/ofp13.c
+++ b/usr.sbin/switchd/ofp13.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ofp13.c,v 1.39 2016/11/21 19:33:12 rzalamena Exp $ */
+/* $OpenBSD: ofp13.c,v 1.40 2016/11/22 17:21:56 rzalamena Exp $ */
/*
* Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org>
@@ -109,7 +109,7 @@ int ofp13_tablemiss_sendctrl(struct switchd *, struct switch_connection *,
uint8_t);
struct ofp_callback ofp13_callbacks[] = {
- { OFP_T_HELLO, ofp13_hello, NULL },
+ { OFP_T_HELLO, ofp13_hello, ofp_validate_hello },
{ OFP_T_ERROR, NULL, ofp13_validate_error },
{ OFP_T_ECHO_REQUEST, ofp13_echo_request, NULL },
{ OFP_T_ECHO_REPLY, NULL, NULL },
@@ -639,13 +639,8 @@ ofp13_hello(struct switchd *sc, struct switch_connection *con,
return (-1);
}
- /* Echo back the received Hello packet */
- oh->oh_version = OFP_V_1_3;
- oh->oh_length = htons(sizeof(*oh));
- oh->oh_xid = htonl(con->con_xidnxt++);
- if (ofp13_validate(sc, &con->con_local, &con->con_peer, oh, NULL) != 0)
+ if (ofp_recv_hello(sc, con, oh, ibuf) == -1)
return (-1);
- ofp_output(con, oh, NULL);
/* Ask for switch features so we can get more information. */
if (ofp13_featuresrequest(sc, con) == -1)
diff --git a/usr.sbin/switchd/ofp_common.c b/usr.sbin/switchd/ofp_common.c
index 019f3c652ca..8fdcbd067d2 100644
--- a/usr.sbin/switchd/ofp_common.c
+++ b/usr.sbin/switchd/ofp_common.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ofp_common.c,v 1.7 2016/11/17 13:10:26 reyk Exp $ */
+/* $OpenBSD: ofp_common.c,v 1.8 2016/11/22 17:21:56 rzalamena Exp $ */
/*
* Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org>
@@ -43,6 +43,8 @@
#include "switchd.h"
#include "ofp_map.h"
+int ofp_setversion(struct switch_connection *, int);
+
int
ofp_validate_header(struct switchd *sc,
struct sockaddr_storage *src, struct sockaddr_storage *dst,
@@ -118,6 +120,176 @@ ofp_output(struct switch_connection *con, struct ofp_header *oh,
return (0);
}
+int
+ofp_send_hello(struct switchd *sc, struct switch_connection *con, int version)
+{
+ struct ofp_hello_element_header *he;
+ struct ofp_header *oh;
+ struct ibuf *ibuf;
+ size_t hstart, hend;
+ uint32_t *bmp;
+ int rv = -1;
+
+ if ((ibuf = ibuf_static()) == NULL ||
+ (oh = ibuf_advance(ibuf, sizeof(*oh))) == NULL ||
+ (he = ibuf_advance(ibuf, sizeof(*he))) == NULL)
+ goto done;
+
+ /* Write down all versions we support. */
+ hstart = ibuf->wpos;
+ if ((bmp = ibuf_advance(ibuf, sizeof(*bmp))) == NULL)
+ goto done;
+
+ *bmp = htonl((1 << OFP_V_1_0) | (1 << OFP_V_1_3));
+ hend = ibuf->wpos;
+
+ /* Fill the headers. */
+ oh->oh_version = version;
+ oh->oh_type = OFP_T_HELLO;
+ oh->oh_length = htons(ibuf_length(ibuf));
+ oh->oh_xid = htonl(con->con_xidnxt++);
+ he->he_type = htons(OFP_HELLO_T_VERSION_BITMAP);
+ he->he_length = htons(sizeof(*he) + (hend - hstart));
+
+ if (ofp_validate(sc, &con->con_local, &con->con_peer, oh, ibuf,
+ version) != 0)
+ goto done;
+
+ rv = ofp_output(con, NULL, ibuf);
+
+ done:
+ ibuf_free(ibuf);
+ return (rv);
+}
+
+int
+ofp_validate_hello(struct switchd *sc,
+ struct sockaddr_storage *src, struct sockaddr_storage *dst,
+ struct ofp_header *oh, struct ibuf *ibuf)
+{
+ struct ofp_hello_element_header *he;
+ uint32_t *bmp;
+ off_t poff;
+ int helen, i, ver;
+
+ /* No extra element headers. */
+ if (ntohs(oh->oh_length) == sizeof(*oh))
+ return (0);
+
+ /* Test for supported element headers. */
+ if ((he = ibuf_seek(ibuf, sizeof(*oh), sizeof(*he))) == NULL)
+ return (-1);
+ if (he->he_type != htons(OFP_HELLO_T_VERSION_BITMAP))
+ return (-1);
+
+ log_debug("\tversion bitmap:");
+
+ /* Validate header sizes. */
+ helen = ntohs(he->he_length);
+ if (helen < (int)sizeof(*he))
+ return (-1);
+ else if (helen == sizeof(*he))
+ return (0);
+
+ helen -= sizeof(*he);
+ /* Invalid bitmap size. */
+ if ((helen % sizeof(*bmp)) != 0)
+ return (-1);
+
+ ver = 0;
+ poff = sizeof(*oh) + sizeof(*he);
+ while (helen > 0) {
+ if ((bmp = ibuf_seek(ibuf, poff, sizeof(*bmp))) == NULL)
+ return (-1);
+
+ for (i = 0; i < 32; i++, ver++) {
+ if ((ntohl(*bmp) & (1 << i)) == 0)
+ continue;
+
+ log_debug("\t\tversion %s",
+ print_map(ver, ofp_v_map));
+ }
+
+ helen -= sizeof(*bmp);
+ poff += sizeof(*bmp);
+ }
+
+ return (0);
+}
+
+int
+ofp_setversion(struct switch_connection *con, int version)
+{
+ switch (version) {
+ case OFP_V_1_0:
+ case OFP_V_1_3:
+ con->con_version = version;
+ return (0);
+
+ default:
+ return (-1);
+ }
+}
+
+int
+ofp_recv_hello(struct switchd *sc, struct switch_connection *con,
+ struct ofp_header *oh, struct ibuf *ibuf)
+{
+ struct ofp_hello_element_header *he;
+ uint32_t *bmp;
+ off_t poff;
+ int helen, i, ver;
+
+ /* No extra element headers, just use the header version. */
+ if (ntohs(oh->oh_length) == sizeof(*oh))
+ return (ofp_setversion(con, oh->oh_version));
+
+ /* Read the element header. */
+ if ((he = ibuf_seek(ibuf, sizeof(*oh), sizeof(*he))) == NULL)
+ return (-1);
+
+ /* We don't support anything else than the version bitmap. */
+ if (he->he_type != htons(OFP_HELLO_T_VERSION_BITMAP))
+ return (-1);
+
+ /* Validate header sizes. */
+ helen = ntohs(he->he_length);
+ if (helen < (int)sizeof(*he))
+ return (-1);
+ else if (helen == sizeof(*he))
+ return (ofp_setversion(con, oh->oh_version));
+
+ helen -= sizeof(*he);
+ /* Invalid bitmap size. */
+ if ((helen % sizeof(*bmp)) != 0)
+ return (-1);
+
+ ver = 0;
+ poff = sizeof(*oh) + sizeof(*he);
+
+ /* Loop through the bitmaps and choose the higher version. */
+ while (helen > 0) {
+ if ((bmp = ibuf_seek(ibuf, poff, sizeof(*bmp))) == NULL)
+ return (-1);
+
+ for (i = 0; i < 32; i++, ver++) {
+ if ((ntohl(*bmp) & (1 << i)) == 0)
+ continue;
+
+ ofp_setversion(con, ver);
+ }
+
+ helen -= sizeof(*bmp);
+ poff += sizeof(*bmp);
+ }
+
+ /* Check if we have set any version, otherwise fallback. */
+ if (con->con_version == OFP_V_0)
+ return (ofp_setversion(con, oh->oh_version));
+
+ return (0);
+}
+
/* Appends an action with just the generic header. */
int
action_new(struct ibuf *ibuf, uint16_t type)
diff --git a/usr.sbin/switchd/switchd.h b/usr.sbin/switchd/switchd.h
index 8b3f73e9523..fb6748688ae 100644
--- a/usr.sbin/switchd/switchd.h
+++ b/usr.sbin/switchd/switchd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: switchd.h,v 1.25 2016/11/18 16:49:35 reyk Exp $ */
+/* $OpenBSD: switchd.h,v 1.26 2016/11/22 17:21:56 rzalamena Exp $ */
/*
* Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org>
@@ -93,6 +93,7 @@ struct switch_connection {
struct sockaddr_storage con_local;
in_port_t con_port;
uint32_t con_xidnxt;
+ int con_version;
struct event con_ev;
struct ibuf *con_rbuf;
@@ -347,6 +348,13 @@ int oflowmod_instructionclose(struct oflowmod_ctx *);
int oflowmod_state(struct oflowmod_ctx *,
unsigned int, unsigned int);
int oflowmod_err(struct oflowmod_ctx *, const char *, int);
+int ofp_validate_hello(struct switchd *,
+ struct sockaddr_storage *, struct sockaddr_storage *,
+ struct ofp_header *, struct ibuf *);
+int ofp_recv_hello(struct switchd *, struct switch_connection *,
+ struct ofp_header *, struct ibuf *);
+int ofp_send_hello(struct switchd *, struct switch_connection *,
+ int);
/* ofcconn.c */
void ofcconn(struct privsep *, struct privsep_proc *);