summaryrefslogtreecommitdiffstats
path: root/usr.sbin/switchd/ofp_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/switchd/ofp_common.c')
-rw-r--r--usr.sbin/switchd/ofp_common.c174
1 files changed, 173 insertions, 1 deletions
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)