Teach switchd(8) how to negotiate protocol version using the hello bitmap
header. This way switchd(8) is able to fallback or use higher version using
the bitmap.
This diff also prevents connections from switching version in the middle of
the operation.
This is the first step before adding a state machine to switchd(8): move the
hello to a common function and make it step into the first state: HELLO_WAIT.
(next diff)
ok?
Index: ofp.c
===================================================================
RCS file: /home/obsdcvs/src/usr.sbin/switchd/ofp.c,v
retrieving revision 1.15
diff -u -p -r1.15 ofp.c
--- ofp.c 4 Nov 2016 22:27:08 -0000 1.15
+++ ofp.c 22 Nov 2016 14:55:12 -0000
@@ -132,6 +132,13 @@ ofp_input(struct switch_connection *con,
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)
@@ -165,6 +172,10 @@ ofp_open(struct privsep *ps, struct swit
log_info("%s: new connection %u.%u from switch %u",
__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);
}
Index: ofp10.c
===================================================================
RCS file: /home/obsdcvs/src/usr.sbin/switchd/ofp10.c,v
retrieving revision 1.16
diff -u -p -r1.16 ofp10.c
--- ofp10.c 21 Nov 2016 18:19:51 -0000 1.16
+++ ofp10.c 22 Nov 2016 15:08:02 -0000
@@ -60,7 +60,7 @@ int ofp10_validate_packet_out(struct sw
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 s
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));
Index: ofp13.c
===================================================================
RCS file: /home/obsdcvs/src/usr.sbin/switchd/ofp13.c,v
retrieving revision 1.39
diff -u -p -r1.39 ofp13.c
--- ofp13.c 21 Nov 2016 19:33:12 -0000 1.39
+++ ofp13.c 22 Nov 2016 14:49:27 -0000
@@ -109,7 +109,7 @@ int ofp13_tablemiss_sendctrl(struct swi
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 s
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)
Index: ofp_common.c
===================================================================
RCS file: /home/obsdcvs/src/usr.sbin/switchd/ofp_common.c,v
retrieving revision 1.7
diff -u -p -r1.7 ofp_common.c
--- ofp_common.c 17 Nov 2016 13:10:26 -0000 1.7
+++ ofp_common.c 22 Nov 2016 14:53:45 -0000
@@ -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,
@@ -114,6 +116,177 @@ ofp_output(struct switch_connection *con
}
ofrelay_write(con, buf);
+
+ 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, vmajor, vminor;
+
+ /* 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 = 9;
+ 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;
+
+ vmajor = (ver / 10);
+ vminor = ver % 10;
+ log_debug("\t\tv%d.%d", vmajor, vminor);
+ }
+
+ 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);
}
Index: switchd.h
===================================================================
RCS file: /home/obsdcvs/src/usr.sbin/switchd/switchd.h,v
retrieving revision 1.25
diff -u -p -r1.25 switchd.h
--- switchd.h 18 Nov 2016 16:49:35 -0000 1.25
+++ switchd.h 22 Nov 2016 14:54:36 -0000
@@ -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 o
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 *);