diff options
Diffstat (limited to 'usr.sbin/switchd/ofp13.c')
-rw-r--r-- | usr.sbin/switchd/ofp13.c | 347 |
1 files changed, 232 insertions, 115 deletions
diff --git a/usr.sbin/switchd/ofp13.c b/usr.sbin/switchd/ofp13.c index 3aacbcd4154..e16381bf5ad 100644 --- a/usr.sbin/switchd/ofp13.c +++ b/usr.sbin/switchd/ofp13.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ofp13.c,v 1.41 2016/12/02 14:39:46 rzalamena Exp $ */ +/* $OpenBSD: ofp13.c,v 1.42 2016/12/22 15:31:43 rzalamena Exp $ */ /* * Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org> @@ -70,10 +70,8 @@ int ofp13_packet_in(struct switchd *, struct switch_connection *, struct ofp_header *, struct ibuf *); int ofp13_flow_removed(struct switchd *, struct switch_connection *, struct ofp_header *, struct ibuf *); -int ofp13_parse_instruction(struct ibuf *, struct ofp_instruction *); -int ofp13_parse_action(struct ibuf *, struct ofp_action_header *); -int ofp13_parse_oxm(struct ibuf *, struct ofp_ox_match *); -int ofp13_parse_tableproperties(struct ibuf *, struct ofp_table_features *); +int ofp13_tableproperties(struct switch_connection *, struct ibuf *, + off_t, size_t, int); int ofp13_multipart_reply(struct switchd *, struct switch_connection *, struct ofp_header *, struct ibuf *); int ofp13_validate_tableproperty(struct ibuf *, off_t, int); @@ -104,6 +102,9 @@ int ofp13_setconfig_validate(struct switchd *, struct sockaddr_storage *, struct sockaddr_storage *, struct ofp_header *, struct ibuf *); +int ofp13_switchconfigure(struct switchd *, struct switch_connection *); +int ofp13_getflowtable(struct switch_connection *); + struct ofp_callback ofp13_callbacks[] = { { OFP_T_HELLO, ofp13_hello, ofp_validate_hello }, { OFP_T_ERROR, NULL, ofp13_validate_error }, @@ -1013,7 +1014,7 @@ ofp13_packet_in(struct switchd *sc, struct switch_connection *con, struct ofp_ox_match *oxm; struct packet pkt; struct ibuf *obuf = NULL; - int ret = -1; + int table, ret = -1; ssize_t len, mlen; uint32_t srcport = 0, dstport; int addflow = 0, sendbuffer = 0; @@ -1091,6 +1092,13 @@ ofp13_packet_in(struct switchd *sc, struct switch_connection *con, again: if (addflow) { + table = ofp13_getflowtable(con); + if (table > OFP_TABLE_ID_MAX || table < 0) { + /* This switch doesn't support installing flows. */ + addflow = 0; + goto again; + } + if ((fm = ibuf_advance(obuf, sizeof(*fm))) == NULL) goto done; @@ -1101,6 +1109,7 @@ ofp13_packet_in(struct switchd *sc, struct switch_connection *con, fm->fm_hard_timeout = 0; /* permanent */ fm->fm_priority = 0; fm->fm_buffer_id = pin->pin_buffer_id; + fm->fm_table_id = table; fm->fm_flags = htons(OFP_FLOWFLAG_SEND_FLOW_REMOVED); if (pin->pin_buffer_id == htonl(OFP_PKTOUT_NO_BUFFER)) sendbuffer = 1; @@ -1191,83 +1200,50 @@ ofp13_flow_removed(struct switchd *sc, struct switch_connection *con, } int -ofp13_parse_instruction(struct ibuf *ibuf, struct ofp_instruction *i) -{ - int type; - int len; - - type = ntohs(i->i_type); - len = ntohs(i->i_len); - - log_debug("\t\t%s", print_map(type, ofp_instruction_t_map)); - - return (len); -} - -int -ofp13_parse_action(struct ibuf *ibuf, struct ofp_action_header *ah) -{ - int len, type; - - len = htons(ah->ah_len); - type = htons(ah->ah_type); - - log_debug("\t\t%s", print_map(type, ofp_action_map)); - - return (len); -} - -int -ofp13_parse_oxm(struct ibuf *ibuf, struct ofp_ox_match *oxm) +ofp13_tableproperties(struct switch_connection *con, struct ibuf *ibuf, + off_t off, size_t total, int new) { - int length, type, class, hasmask; + struct ofp_table_features *tf; + struct ofp_table_feature_property *tp; + struct ofp_instruction *i; + struct ofp_action_header *ah; + struct ofp_ox_match *oxm; + struct switch_table *st; + uint8_t *next_table; + int remaining, type, length; + int hlen, padsize; + int class, dtype, dlen; - class = ntohs(oxm->oxm_class); - type = OFP_OXM_GET_FIELD(oxm); - hasmask = OFP_OXM_GET_HASMASK(oxm); /* - * XXX the OpenFlow 1.3.5 specification says this field is only - * 4 bytes long, however the experimental type is 8 bytes. + * This is a new table features reply, free our previous tables + * to get the updated ones. */ - length = sizeof(*oxm); + if (new) + switch_freetables(con); - log_debug("\t\t%s hasmask %s type %s", - print_map(class, ofp_oxm_c_map), hasmask ? "yes" : "no", - print_map(type, ofp_xm_t_map)); + next_table: + if ((tf = ibuf_seek(ibuf, off, sizeof(*tf))) == NULL) + return (-1); - if (class == OFP_OXM_C_OPENFLOW_EXPERIMENTER) { - /* Get the last bytes. */ - if (ibuf_getdata(ibuf, 4) == NULL) - return (-1); + hlen = htons(tf->tf_length); + total -= hlen; + remaining = hlen - sizeof(*tf); + off += sizeof(*tf); - return (8); + st = switch_tablelookup(con, tf->tf_tableid); + if (st == NULL) { + st = switch_newtable(con, tf->tf_tableid); + if (st == NULL) + return (-1); } - return (length); -} - -int -ofp13_parse_tableproperties(struct ibuf *ibuf, struct ofp_table_features *tf) -{ - struct ofp_table_feature_property *tp; - struct ofp_instruction *i; - struct ofp_action_header *ah; - struct ofp_ox_match *oxm; - uint8_t *next_table; - int remaining, type, length; - int totallen, padsize, rv; - - log_debug("Table %s (%d) max_entries %u config %u " - "metadata match %#016llx write %#016llx", - tf->tf_name, tf->tf_tableid, ntohl(tf->tf_max_entries), - ntohl(tf->tf_config), be64toh(tf->tf_metadata_match), - be64toh(tf->tf_metadata_write)); - totallen = htons(tf->tf_length); - remaining = totallen - sizeof(*tf); + st->st_maxentries = ntohl(tf->tf_max_entries); next_table_property: - if ((tp = ibuf_getdata(ibuf, sizeof(*tp))) == NULL) + if ((tp = ibuf_seek(ibuf, off, sizeof(*tp))) == NULL) { + switch_deltable(con, st); return (-1); + } type = ntohs(tp->tp_type); length = ntohs(tp->tp_length); @@ -1276,32 +1252,63 @@ ofp13_parse_tableproperties(struct ibuf *ibuf, struct ofp_table_features *tf) padsize = OFP_ALIGN(length) - length; remaining -= OFP_ALIGN(length); length -= sizeof(*tp); - - log_debug("\t%s:", print_map(type, ofp_table_featprop_map)); - if (length == 0) - log_debug("\t\tNONE"); + off += sizeof(*tp); switch (type) { case OFP_TABLE_FEATPROP_INSTRUCTION: case OFP_TABLE_FEATPROP_INSTRUCTION_MISS: + if (type == OFP_TABLE_FEATPROP_INSTRUCTION) + st->st_instructions = 0; + else + st->st_instructionsmiss = 0; + while (length) { - if ((i = ibuf_getdata(ibuf, sizeof(*i))) == NULL) - return (-1); - if ((rv = ofp13_parse_instruction(ibuf, i)) == -1) + if ((i = ibuf_seek(ibuf, off, sizeof(*i))) == NULL) { + switch_deltable(con, st); return (-1); - length -= rv; + } + + dtype = ntohs(i->i_type); + dlen = ntohs(i->i_len); + if (type == OFP_TABLE_FEATPROP_INSTRUCTION) + st->st_instructions |= 1ULL << dtype; + else + st->st_instructionsmiss |= 1ULL << dtype; + + if (dtype == OFP_INSTRUCTION_T_EXPERIMENTER) { + length -= dlen; + off += dlen; + } else { + length -= sizeof(*i); + off += sizeof(*i); + } } break; case OFP_TABLE_FEATPROP_NEXT_TABLES: case OFP_TABLE_FEATPROP_NEXT_TABLES_MISS: + if (type == OFP_TABLE_FEATPROP_NEXT_TABLES) + memset(st->st_nexttable, 0, sizeof(st->st_nexttable)); + else + memset(st->st_nexttablemiss, 0, + sizeof(st->st_nexttablemiss)); + while (length) { - if ((next_table = ibuf_getdata(ibuf, - sizeof(*next_table))) == NULL) + if ((next_table = ibuf_seek(ibuf, off, + sizeof(*next_table))) == NULL) { + switch_deltable(con, st); return (-1); + } + + if (type == OFP_TABLE_FEATPROP_NEXT_TABLES) + st->st_nexttable[(*next_table) / 64] |= + 1ULL << ((*next_table) % 64); + else + st->st_nexttablemiss[(*next_table) / 64] |= + 1ULL << ((*next_table) % 64); - log_debug("\t\t%d", *next_table); length -= sizeof(*next_table); + off += sizeof(*next_table); } break; @@ -1309,16 +1316,37 @@ ofp13_parse_tableproperties(struct ibuf *ibuf, struct ofp_table_features *tf) case OFP_TABLE_FEATPROP_WRITE_ACTIONS_MISS: case OFP_TABLE_FEATPROP_APPLY_ACTIONS: case OFP_TABLE_FEATPROP_APPLY_ACTIONS_MISS: + if (type == OFP_TABLE_FEATPROP_WRITE_ACTIONS || + type == OFP_TABLE_FEATPROP_APPLY_ACTIONS) + st->st_actions = 0; + else + st->st_actionsmiss = 0; + while (length) { /* - * XXX the OpenFlow 1.3.5 specs says that we only - * get 4 bytes here instead of the full OXM. + * NOTE the OpenFlow 1.3.5 specs says that we only + * get 4 bytes here instead of the full action header. */ - if ((ah = ibuf_getdata(ibuf, 4)) == NULL) - return (-1); - if ((rv = ofp13_parse_action(ibuf, ah)) == -1) + if ((ah = ibuf_seek(ibuf, off, 4)) == NULL) { + switch_deltable(con, st); return (-1); - length -= rv; + } + + dtype = ntohs(ah->ah_type); + dlen = ntohs(ah->ah_len); + if (type == OFP_TABLE_FEATPROP_WRITE_ACTIONS || + type == OFP_TABLE_FEATPROP_APPLY_ACTIONS) + st->st_actions |= 1ULL << dtype; + else + st->st_actionsmiss |= 1ULL << dtype; + + if (dtype == OFP_ACTION_EXPERIMENTER) { + length -= dlen; + off += dlen; + } else { + length -= 4; + off += 4; + } } break; @@ -1328,32 +1356,72 @@ ofp13_parse_tableproperties(struct ibuf *ibuf, struct ofp_table_features *tf) case OFP_TABLE_FEATPROP_WRITE_SETFIELD_MISS: case OFP_TABLE_FEATPROP_APPLY_SETFIELD: case OFP_TABLE_FEATPROP_APPLY_SETFIELD_MISS: + if (type == OFP_TABLE_FEATPROP_WRITE_SETFIELD || + type == OFP_TABLE_FEATPROP_APPLY_SETFIELD) + st->st_setfield = 0; + else if (type == OFP_TABLE_FEATPROP_WRITE_SETFIELD_MISS || + type == OFP_TABLE_FEATPROP_APPLY_SETFIELD_MISS) + st->st_setfieldmiss = 0; + else if (type == OFP_TABLE_FEATPROP_MATCH) + st->st_match = 0; + else + st->st_wildcard = 0; + while (length) { - if ((oxm = ibuf_getdata(ibuf, sizeof(*oxm))) == NULL) - return (-1); - if ((rv = ofp13_parse_oxm(ibuf, oxm)) == -1) + if ((oxm = ibuf_seek(ibuf, off, + sizeof(*oxm))) == NULL) { + switch_deltable(con, st); return (-1); - length -= rv; - } - break; + } - default: - log_debug("%s: unsupported property type: %d", __func__, type); + class = ntohs(oxm->oxm_class); + if (class != OFP_OXM_C_OPENFLOW_BASIC) { + if (class == OFP_OXM_C_OPENFLOW_EXPERIMENTER) { + length -= sizeof(*oxm) + 4; + off += sizeof(*oxm) + 4; + } else { + length -= sizeof(*oxm); + off += sizeof(*oxm); + } + continue; + } - /* Skip this field and try to continue otherwise fail. */ - if (ibuf_getdata(ibuf, length) == NULL) - return (-1); + dtype = OFP_OXM_GET_FIELD(oxm); + if (type == OFP_TABLE_FEATPROP_WRITE_SETFIELD || + type == OFP_TABLE_FEATPROP_APPLY_SETFIELD) + st->st_setfield |= 1ULL << dtype; + else if + (type == OFP_TABLE_FEATPROP_WRITE_SETFIELD_MISS || + type == OFP_TABLE_FEATPROP_APPLY_SETFIELD_MISS) + st->st_setfieldmiss |= 1ULL << dtype; + else if (type == OFP_TABLE_FEATPROP_MATCH) + st->st_match |= 1ULL << dtype; + else + st->st_wildcard |= 1ULL << dtype; + length -= sizeof(*oxm); + off += sizeof(*oxm); + } break; - } - /* Skip the padding and read the next property if any. */ - if (padsize && ibuf_getdata(ibuf, padsize) == NULL) + case OFP_TABLE_FEATPROP_EXPERIMENTER: + case OFP_TABLE_FEATPROP_EXPERIMENTER_MISS: + off += length; + break; + + default: + log_debug("Unsupported table property %d", type); return (-1); - if (remaining) + } + + if (padsize) + off += padsize; + if (remaining > 0) goto next_table_property; + if (total > 0) + goto next_table; - return (totallen); + return (0); } int @@ -1361,18 +1429,25 @@ ofp13_multipart_reply(struct switchd *sc, struct switch_connection *con, struct ofp_header *oh, struct ibuf *ibuf) { struct ofp_multipart *mp; - struct ofp_table_features *tf; - int readlen, type, flags; + int type, flags, more, new = 0; int remaining; + off_t off; - if ((mp = ibuf_getdata(ibuf, sizeof(*mp))) == NULL) + off = 0; + if ((mp = ibuf_seek(ibuf, 0, sizeof(*mp))) == NULL) return (-1); type = ntohs(mp->mp_type); flags = ntohs(mp->mp_flags); remaining = ntohs(oh->oh_length) - sizeof(*mp); + off += sizeof(*mp); + + more = (flags & OFP_MP_FLAG_REPLY_MORE) == OFP_MP_FLAG_REPLY_MORE; + /* Signalize new requests. */ + if (ofp_multipart_lookup(con, oh->oh_xid) == NULL) + new = 1; - if (flags & OFP_MP_FLAG_REPLY_MORE) { + if (more) { if (ofp_multipart_add(con, oh->oh_xid, type) == -1) { ofp13_error(sc, con, oh, ibuf, OFP_ERRTYPE_BAD_REQUEST, @@ -1393,15 +1468,12 @@ ofp13_multipart_reply(struct switchd *sc, struct switch_connection *con, switch (type) { case OFP_MP_T_TABLE_FEATURES: - read_next_table: - if ((tf = ibuf_getdata(ibuf, sizeof(*tf))) == NULL) - return (-1); - if ((readlen = ofp13_parse_tableproperties(ibuf, tf)) == -1) + if (ofp13_tableproperties(con, ibuf, off, remaining, new)) return (-1); - remaining -= readlen; - if (remaining) - goto read_next_table; + /* We finished receiving tables, configure the switch. */ + if (more == 0) + return (ofp13_switchconfigure(sc, con)); break; } @@ -2137,3 +2209,48 @@ ofp13_tablemiss_sendctrl(struct switchd *sc, struct switch_connection *con, (void)oflowmod_err(&ctx, __func__, __LINE__); return (-1); } + +int +ofp13_switchconfigure(struct switchd *sc, struct switch_connection *con) +{ + struct switch_table *st; + + /* Look for a table with 'apply' and 'output' support for miss. */ + TAILQ_FOREACH(st, &con->con_stlist, st_entry) { + if ((st->st_instructionsmiss & + (1ULL << OFP_INSTRUCTION_T_APPLY_ACTIONS)) == 0) + continue; + if ((st->st_actionsmiss & (1ULL << OFP_ACTION_OUTPUT)) == 0) + continue; + + break; + } + if (st == NULL) { + log_warn("No apply output for this switch"); + return (-1); + } + + /* Install the flow to receive packets from the switch. */ + return (ofp13_tablemiss_sendctrl(sc, con, st->st_table)); +} + +int +ofp13_getflowtable(struct switch_connection *con) +{ + struct switch_table *st; + + /* Look for a table with 'apply' and 'output' support. */ + TAILQ_FOREACH(st, &con->con_stlist, st_entry) { + if ((st->st_instructions & + (1ULL << OFP_INSTRUCTION_T_APPLY_ACTIONS)) == 0) + continue; + if ((st->st_actions & (1ULL << OFP_ACTION_OUTPUT)) == 0) + continue; + + break; + } + if (st == NULL) + return (-1); + + return (st->st_table); +} |