> On 22.11.2016, at 16:12, Rafael Zalamena <[email protected]> wrote:
> 
> 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?

Two comments below, otherwise OK.

Reyk

> 
> 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 &&

This should be handled by the state machine later, but OK for now.

(<= HELLO_WAIT accepts hello and any version, > HELLO_WAIT doesn't)

> +         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);

this should be

        log_debug("\t\tversion %s", print_map(ofp_v_map, ver));

no need for 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 *);
> 

Reply via email to