diff options
author | Rafael Zalamena <rzalamena@cvs.openbsd.org> | 2016-09-26 12:33:05 +0000 |
---|---|---|
committer | Rafael Zalamena <rzalamena@cvs.openbsd.org> | 2016-09-26 12:33:05 +0000 |
commit | c7bcee12fae570d16ba3ba268157a443400e2310 (patch) | |
tree | 8a5d56e937abcf3c8262b1c7af60feeed21af5db /usr.sbin | |
parent | 940ab56dc3e3ac584c55e3e14dc1e7de4d7b2351 (diff) |
Teach switchd(8) how to create flows for new connections using OpenFlow
1.3.5, implement the OXM filters to use with flow matching and Set-Action,
prepare code to receive group management and add dummy flow_removed handler
to avoid closing the connection on idle flows.
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/switchd/ofp.h | 129 | ||||
-rw-r--r-- | usr.sbin/switchd/ofp13.c | 1027 |
2 files changed, 1054 insertions, 102 deletions
diff --git a/usr.sbin/switchd/ofp.h b/usr.sbin/switchd/ofp.h index 7b01b9f323b..ed8c3a55997 100644 --- a/usr.sbin/switchd/ofp.h +++ b/usr.sbin/switchd/ofp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ofp.h,v 1.3 2016/08/25 11:00:44 reyk Exp $ */ +/* $OpenBSD: ofp.h,v 1.4 2016/09/26 12:33:04 rzalamena Exp $ */ /* * Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org> @@ -249,8 +249,8 @@ struct ofp_instruction_goto_table { struct ofp_instruction_actions { uint16_t ia_type; uint16_t ia_len; - uint8_t pad[4]; -}; + uint8_t ia_pad[4]; +} __packed; /* Meter instruction */ struct ofp_instruction_meter { @@ -268,21 +268,21 @@ struct ofp_instruction_experimenter { /* Actions */ #define OFP_ACTION_OUTPUT 0 /* Output to switch port */ -#define OFP_ACTION_COPY_TTL_OUT 11 /* ? */ -#define OFP_ACTION_COPY_TTL_IN 12 /* ? */ -#define OFP_ACTION_SET_MPLS_TTL 15 /* ? */ -#define OFP_ACTION_DEC_MPLS_TTL 16 /* ? */ -#define OFP_ACTION_PUSH_VLAN 17 /* ? */ -#define OFP_ACTION_POP_VLAN 18 /* ? */ -#define OFP_ACTION_PUSH_MPLS 19 /* ? */ -#define OFP_ACTION_POP_MPLS 20 /* ? */ -#define OFP_ACTION_SET_QUEUE 21 /* ? */ -#define OFP_ACTION_GROUP 22 /* ? */ -#define OFP_ACTION_SET_NW_TTL 23 /* ? */ -#define OFP_ACTION_DEC_NW_TTL 24 /* ? */ -#define OFP_ACTION_SET_FIELD 25 /* ? */ -#define OFP_ACTION_PUSH_PBB 26 /* ? */ -#define OFP_ACTION_POP_PBB 27 /* ? */ +#define OFP_ACTION_COPY_TTL_OUT 11 /* Copy TTL outwards */ +#define OFP_ACTION_COPY_TTL_IN 12 /* Copy TTL inwards */ +#define OFP_ACTION_SET_MPLS_TTL 15 /* MPLS TTL */ +#define OFP_ACTION_DEC_MPLS_TTL 16 /* Decrement MPLS TTL */ +#define OFP_ACTION_PUSH_VLAN 17 /* Push a new VLAN tag */ +#define OFP_ACTION_POP_VLAN 18 /* Pop the outer VLAN tag */ +#define OFP_ACTION_PUSH_MPLS 19 /* Push a new MPLS tag */ +#define OFP_ACTION_POP_MPLS 20 /* Pop the outer MPLS tag */ +#define OFP_ACTION_SET_QUEUE 21 /* Set queue id when outputing to a port */ +#define OFP_ACTION_GROUP 22 /* Apply group */ +#define OFP_ACTION_SET_NW_TTL 23 /* Set IP TTL */ +#define OFP_ACTION_DEC_NW_TTL 24 /* Decrement IP TTL */ +#define OFP_ACTION_SET_FIELD 25 /* Set a header field using OXM TLV format */ +#define OFP_ACTION_PUSH_PBB 26 /* Push a new PBB service tag (I-TAG) */ +#define OFP_ACTION_POP_PBB 27 /* Pop the outer PBB service tag (I-TAG) */ #define OFP_ACTION_EXPERIMENTER 0xffff /* Vendor-specific action */ /* Action Header */ @@ -355,47 +355,49 @@ struct ofp_packet_out { /* Followed by optional packet data if buffer_id == 0xffffffff */ } __packed; +#define OFP_PKTOUT_NO_BUFFER 0xffffffff + /* Flow match fields for basic class */ -#define OFP_XM_T_IN_PORT 0 /* ? */ -#define OFP_XM_T_IN_PHY_PORT 1 /* ? */ -#define OFP_XM_T_META 2 /* ? */ -#define OFP_XM_T_ETH_DST 3 /* ? */ -#define OFP_XM_T_ETH_SRC 4 /* ? */ -#define OFP_XM_T_ETH_TYPE 5 /* ? */ -#define OFP_XM_T_VLAN_VID 6 /* ? */ -#define OFP_XM_T_VLAN_PCP 7 /* ? */ -#define OFP_XM_T_IP_DSCP 8 /* ? */ -#define OFP_XM_T_IP_ECN 9 /* ? */ -#define OFP_XM_T_IP_PROTO 10 /* ? */ -#define OFP_XM_T_IPV4_SRC 11 /* ? */ -#define OFP_XM_T_IPV4_DST 12 /* ? */ -#define OFP_XM_T_TCP_SRC 13 /* ? */ -#define OFP_XM_T_TCP_DST 14 /* ? */ -#define OFP_XM_T_UDP_SRC 15 /* ? */ -#define OFP_XM_T_UDP_DST 16 /* ? */ -#define OFP_XM_T_SCTP_SRC 17 /* ? */ -#define OFP_XM_T_SCTP_DST 18 /* ? */ -#define OFP_XM_T_ICMPV4_TYPE 19 /* ? */ -#define OFP_XM_T_ICMPV4_CODE 20 /* ? */ -#define OFP_XM_T_ARP_OP 21 /* ? */ -#define OFP_XM_T_ARP_SPA 22 /* ? */ -#define OFP_XM_T_ARP_TPA 23 /* ? */ -#define OFP_XM_T_ARP_SHA 24 /* ? */ -#define OFP_XM_T_ARP_THA 25 /* ? */ -#define OFP_XM_T_IPV6_SRC 26 /* ? */ -#define OFP_XM_T_IPV6_DST 27 /* ? */ -#define OFP_XM_T_IPV6_FLABEL 28 /* ? */ -#define OFP_XM_T_ICMPV6_TYPE 29 /* ? */ -#define OFP_XM_T_ICMPV6_CODE 30 /* ? */ -#define OFP_XM_T_IPV6_ND_TARGET 31 /* ? */ -#define OFP_XM_T_IPV6_ND_SLL 32 /* ? */ -#define OFP_XM_T_IPV6_ND_TLL 33 /* ? */ -#define OFP_XM_T_MPLS_LABEL 34 /* ? */ -#define OFP_XM_T_MPLS_TC 35 /* ? */ -#define OFP_XM_T_MPLS_BOS 36 /* ? */ -#define OFP_XM_T_PBB_ISID 37 /* ? */ -#define OFP_XM_T_TUNNEL_ID 38 /* ? */ -#define OFP_XM_T_IPV6_EXTHDR 39 /* ? */ +#define OFP_XM_T_IN_PORT 0 /* Switch input port */ +#define OFP_XM_T_IN_PHY_PORT 1 /* Switch physical input port */ +#define OFP_XM_T_META 2 /* Metadata passed between tables */ +#define OFP_XM_T_ETH_DST 3 /* Ethernet destination address */ +#define OFP_XM_T_ETH_SRC 4 /* Ethernet source address */ +#define OFP_XM_T_ETH_TYPE 5 /* Ethernet frame type */ +#define OFP_XM_T_VLAN_VID 6 /* VLAN id */ +#define OFP_XM_T_VLAN_PCP 7 /* VLAN priority */ +#define OFP_XM_T_IP_DSCP 8 /* IP DSCP (6 bits in ToS field) */ +#define OFP_XM_T_IP_ECN 9 /* IP ECN (2 bits in ToS field) */ +#define OFP_XM_T_IP_PROTO 10 /* IP protocol */ +#define OFP_XM_T_IPV4_SRC 11 /* IPv4 source address */ +#define OFP_XM_T_IPV4_DST 12 /* IPv4 destination address */ +#define OFP_XM_T_TCP_SRC 13 /* TCP source port */ +#define OFP_XM_T_TCP_DST 14 /* TCP destination port */ +#define OFP_XM_T_UDP_SRC 15 /* UDP source port */ +#define OFP_XM_T_UDP_DST 16 /* UDP destination port */ +#define OFP_XM_T_SCTP_SRC 17 /* SCTP source port */ +#define OFP_XM_T_SCTP_DST 18 /* SCTP destination port */ +#define OFP_XM_T_ICMPV4_TYPE 19 /* ICMP type */ +#define OFP_XM_T_ICMPV4_CODE 20 /* ICMP code */ +#define OFP_XM_T_ARP_OP 21 /* ARP opcode */ +#define OFP_XM_T_ARP_SPA 22 /* ARP source IPv4 address */ +#define OFP_XM_T_ARP_TPA 23 /* ARP target IPv4 address */ +#define OFP_XM_T_ARP_SHA 24 /* ARP source hardware address */ +#define OFP_XM_T_ARP_THA 25 /* ARP target hardware address */ +#define OFP_XM_T_IPV6_SRC 26 /* IPv6 source address */ +#define OFP_XM_T_IPV6_DST 27 /* IPv6 destination address */ +#define OFP_XM_T_IPV6_FLABEL 28 /* IPv6 Flow Label */ +#define OFP_XM_T_ICMPV6_TYPE 29 /* ICMPv6 type */ +#define OFP_XM_T_ICMPV6_CODE 30 /* ICMPv6 code */ +#define OFP_XM_T_IPV6_ND_TARGET 31 /* Target address for ND */ +#define OFP_XM_T_IPV6_ND_SLL 32 /* Source link-layer for ND */ +#define OFP_XM_T_IPV6_ND_TLL 33 /* Target link-layer for ND */ +#define OFP_XM_T_MPLS_LABEL 34 /* MPLS label */ +#define OFP_XM_T_MPLS_TC 35 /* MPLS TC */ +#define OFP_XM_T_MPLS_BOS 36 /* MPLS BoS bit */ +#define OFP_XM_T_PBB_ISID 37 /* PBB I-SID */ +#define OFP_XM_T_TUNNEL_ID 38 /* Logical Port Metadata */ +#define OFP_XM_T_IPV6_EXTHDR 39 /* IPv6 Extension Header pseudo-field */ #define OFP_XM_T_MAX 40 /* ? */ /* Flow match fields for nxm1 class */ @@ -415,10 +417,21 @@ struct ofp_packet_out { #define OFP_XM_VID_PRESENT 0x1000 /* VLAN ID present */ #define OFP_XM_VID_NONE 0x0000 /* No VLAN ID */ +#define OFP_XM_IPV6_EXTHDR_NONEXT (1 << 0) +#define OFP_XM_IPV6_EXTHDR_ESP (1 << 1) +#define OFP_XM_IPV6_EXTHDR_AUTH (1 << 2) +#define OFP_XM_IPV6_EXTHDR_DEST (1 << 3) +#define OFP_XM_IPV6_EXTHDR_FRAG (1 << 4) +#define OFP_XM_IPV6_EXTHDR_ROUTER (1 << 5) +#define OFP_XM_IPV6_EXTHDR_HOP (1 << 6) +#define OFP_XM_IPV6_EXTHDR_UNREP (1 << 7) +#define OFP_XM_IPV6_EXTHDR_UNSEQ (1 << 8) + struct ofp_ox_match { uint16_t oxm_class; uint8_t oxm_fh; uint8_t oxm_length; + uint8_t oxm_value[0]; }; #define OFP_OXM_GET_FIELD(o) (((o)->oxm_fh) >> 1) diff --git a/usr.sbin/switchd/ofp13.c b/usr.sbin/switchd/ofp13.c index 80f5f100f4f..a324af35c90 100644 --- a/usr.sbin/switchd/ofp13.c +++ b/usr.sbin/switchd/ofp13.c @@ -1,7 +1,8 @@ -/* $OpenBSD: ofp13.c,v 1.6 2016/09/26 08:46:00 rzalamena Exp $ */ +/* $OpenBSD: ofp13.c,v 1.7 2016/09/26 12:33:04 rzalamena Exp $ */ /* * Copyright (c) 2013-2016 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2016 Rafael Zalamena <rzalamena@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -25,7 +26,9 @@ #include <netinet/in.h> #include <netinet/if_ether.h> #include <netinet/tcp.h> +#include <netmpls/mpls.h> +#include <endian.h> #include <stdio.h> #include <stdlib.h> #include <stddef.h> @@ -43,8 +46,6 @@ int ofp13_validate(struct switchd *, struct sockaddr_storage *, struct sockaddr_storage *, struct ofp_header *, struct ibuf *); -int ofp13_packet_match(struct packet *, struct ofp_match *, unsigned int); - int ofp13_hello(struct switchd *, struct switch_connection *, struct ofp_header *, struct ibuf *); int ofp13_echo_request(struct switchd *, struct switch_connection *, @@ -59,12 +60,66 @@ int ofp13_validate_oxm(struct switchd *, struct ofp_ox_match *, int ofp13_validate_packet_in(struct switchd *, struct sockaddr_storage *, struct sockaddr_storage *, struct ofp_header *, struct ibuf *); +int ofp13_packet_match(struct ibuf *, struct packet *, struct ofp_match *); 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_validate_packet_out(struct switchd *, struct sockaddr_storage *, struct sockaddr_storage *, struct ofp_header *, struct ibuf *); +struct ofp_group_mod * + ofp13_group(struct switch_connection *, struct ibuf *, + uint32_t, uint16_t, uint8_t); +struct ofp_bucket * + ofp13_bucket(struct ibuf *, uint16_t, uint32_t, uint32_t); + +int action_new(struct ibuf *, uint16_t); +int action_group(struct ibuf *, uint32_t); +int action_output(struct ibuf *, uint32_t, uint16_t); +int action_push(struct ibuf *, uint16_t, uint16_t); +int action_pop_vlan(struct ibuf *); +int action_pop_mpls(struct ibuf *, uint16_t); +int action_copyttlout(struct ibuf *); +int action_copyttlin(struct ibuf *); +int action_decnwttl(struct ibuf *); +struct ofp_action_set_field * + action_setfield(struct ibuf *ibuf); + +struct ofp_ox_match * + oxm_get(struct ibuf *, uint16_t, int, uint8_t); +int oxm_inport(struct ibuf *, uint32_t); +int oxm_inphyport(struct ibuf *, uint32_t); +int oxm_metadata(struct ibuf *, int, uint64_t, uint64_t); +int oxm_etheraddr(struct ibuf *, int, uint8_t *, uint8_t *); +int oxm_ethertype(struct ibuf *, uint16_t); +int oxm_vlanvid(struct ibuf *, int, uint16_t, uint16_t); +int oxm_vlanpcp(struct ibuf *, uint8_t); +int oxm_ipdscp(struct ibuf *, uint8_t); +int oxm_ipecn(struct ibuf *, uint8_t); +int oxm_ipproto(struct ibuf *, uint8_t); +int oxm_ipaddr(struct ibuf *, int, int, uint32_t, uint32_t); +int oxm_tcpport(struct ibuf *, int, uint16_t); +int oxm_udpport(struct ibuf *, int, uint16_t); +int oxm_sctpport(struct ibuf *, int, uint16_t); +int oxm_icmpv4type(struct ibuf *, uint8_t); +int oxm_icmpv4code(struct ibuf *, uint8_t); +int oxm_arpop(struct ibuf *, uint16_t); +int oxm_arpaddr(struct ibuf *, int, int, uint32_t, uint32_t); +int oxm_arphaddr(struct ibuf *, int, uint8_t *, uint8_t *); +int oxm_ipv6addr(struct ibuf *, int, struct in6_addr *, struct in6_addr *); +int oxm_ipv6flowlabel(struct ibuf *, int, uint32_t, uint32_t); +int oxm_icmpv6type(struct ibuf *, uint8_t); +int oxm_icmpv6code(struct ibuf *, uint8_t); +int oxm_ipv6ndtarget(struct ibuf *, struct in6_addr *); +int oxm_ipv6ndlinkaddr(struct ibuf *, int, uint8_t *); +int oxm_mplslabel(struct ibuf *, uint32_t); +int oxm_mplstc(struct ibuf *, uint8_t); +int oxm_mplsbos(struct ibuf *, uint8_t); +int oxm_tunnelid(struct ibuf *, int, uint64_t, uint64_t); +int oxm_ipv6exthdr(struct ibuf *, int, uint16_t, uint16_t); + struct ofp_callback ofp13_callbacks[] = { { OFP_T_HELLO, ofp13_hello, NULL }, { OFP_T_ERROR, NULL, ofp13_validate_error }, @@ -78,7 +133,7 @@ struct ofp_callback ofp13_callbacks[] = { { OFP_T_SET_CONFIG, NULL, NULL }, { OFP_T_PACKET_IN, ofp13_packet_in, ofp13_validate_packet_in }, - { OFP_T_FLOW_REMOVED, NULL, NULL }, + { OFP_T_FLOW_REMOVED, ofp13_flow_removed, NULL }, { OFP_T_PORT_STATUS, NULL, NULL }, { OFP_T_PACKET_OUT, NULL, ofp13_validate_packet_out }, { OFP_T_FLOW_MOD, NULL, NULL }, @@ -367,22 +422,34 @@ ofp13_echo_request(struct switchd *sc, struct switch_connection *con, } int -ofp13_packet_match(struct packet *pkt, struct ofp_match *m, uint32_t flags) +ofp13_packet_match(struct ibuf *ibuf, struct packet *pkt, struct ofp_match *om) { -#if 0 struct ether_header *eh = pkt->pkt_eh; + int unalignedsize; + size_t padsize, startpos, endpos, omlen; - bzero(m, sizeof(*m)); - m->m_wildcards = htonl(~flags); + if (eh == NULL) + return (-1); - if ((flags & (OFP_WILDCARD_DL_SRC|OFP_WILDCARD_DL_DST)) && (eh == NULL)) + startpos = ibuf->wpos; + if (oxm_etheraddr(ibuf, 1, eh->ether_shost, NULL) == -1) + return (-1); + if (oxm_etheraddr(ibuf, 0, eh->ether_dhost, NULL) == -1) + return (-1); + endpos = ibuf->wpos; + + omlen = sizeof(*om) + (endpos - startpos); + unalignedsize = (omlen % 8); + if (unalignedsize) + padsize = 8 - unalignedsize; + else + padsize = 0; + + om->om_type = htons(OFP_MATCH_OXM); + om->om_length = htons(omlen); + if (padsize && ibuf_advance(ibuf, padsize) == NULL) return (-1); - if (flags & OFP_WILDCARD_DL_SRC) - memcpy(m->m_dl_src, eh->ether_shost, ETHER_ADDR_LEN); - if (flags & OFP_WILDCARD_DL_DST) - memcpy(m->m_dl_dst, eh->ether_dhost, ETHER_ADDR_LEN); -#endif return (0); } @@ -392,10 +459,7 @@ ofp13_packet_in(struct switchd *sc, struct switch_connection *con, { struct ofp_packet_in *pin; struct ofp_packet_out *pout; - struct ofp_action_output *ao; -#if 0 struct ofp_flow_mod *fm; -#endif struct ofp_header *oh; struct ofp_match *om; struct ofp_ox_match *oxm; @@ -404,14 +468,18 @@ ofp13_packet_in(struct switchd *sc, struct switch_connection *con, int ret = -1; ssize_t len, mlen; uint32_t srcport = 0, dstport; - int addflow = 0; - int addpacket = 0; + int addflow = 0, sendbuffer = 0; off_t off, moff; void *ptr; + struct ofp_instruction_actions *ia; if ((pin = ibuf_getdata(ibuf, sizeof(*pin))) == NULL) return (-1); + /* We only handle no matches right now. */ + if (pin->pin_reason != OFP_PKTIN_REASON_NO_MATCH) + return (-1); + bzero(&pkt, sizeof(pkt)); len = ntohs(pin->pin_total_len); @@ -467,19 +535,16 @@ ofp13_packet_in(struct switchd *sc, struct switch_connection *con, } if (dstport <= OFP_PORT_MAX) - addflow = 0; + addflow = 1; if ((obuf = ibuf_static()) == NULL) goto done; again: if (addflow) { -#if 0 if ((fm = ibuf_advance(obuf, sizeof(*fm))) == NULL) goto done; - ofp13_packet_match(&pkt, &fm->fm_match, OFP_WILDCARD_DL_DST); - oh = &fm->fm_oh; fm->fm_cookie = 0; /* XXX should we set a cookie? */ fm->fm_command = htons(OFP_FLOWCMD_ADD); @@ -488,9 +553,24 @@ ofp13_packet_in(struct switchd *sc, struct switch_connection *con, fm->fm_priority = 0; fm->fm_buffer_id = pin->pin_buffer_id; fm->fm_flags = htons(OFP_FLOWFLAG_SEND_FLOW_REMOVED); - if (pin->pin_buffer_id == (uint32_t)-1) - addpacket = 1; -#endif + if (pin->pin_buffer_id == OFP_PKTOUT_NO_BUFFER) + sendbuffer = 1; + + /* Write flow matches to create an entry. */ + if (ofp13_packet_match(obuf, &pkt, &fm->fm_match) == -1) + goto done; + + /* + * Write the instruction action header and add the output + * action. + */ + if ((ia = ibuf_advance(obuf, sizeof(*ia))) == NULL || + action_output(obuf, dstport, 0) == -1) + goto done; + + ia->ia_type = htons(OFP_INSTRUCTION_T_APPLY_ACTIONS); + ia->ia_len = htons(sizeof(*ia) + + sizeof(struct ofp_action_output)); } else { if ((pout = ibuf_advance(obuf, sizeof(*pout))) == NULL) goto done; @@ -498,26 +578,20 @@ ofp13_packet_in(struct switchd *sc, struct switch_connection *con, oh = &pout->pout_oh; pout->pout_buffer_id = pin->pin_buffer_id; pout->pout_in_port = htonl(srcport); - pout->pout_actions_len = htons(sizeof(*ao)); - - if (pin->pin_buffer_id == (uint32_t)-1) - addpacket = 1; - } + pout->pout_actions_len = + htons(sizeof(struct ofp_action_output)); - if ((ao = ibuf_advance(obuf, sizeof(*ao))) == NULL) - goto done; - ao->ao_type = htons(OFP_ACTION_OUTPUT); - ao->ao_len = htons(sizeof(*ao)); - ao->ao_port = htonl(dstport); - ao->ao_max_len = 0; + if (action_output(obuf, dstport, 0) == -1) + goto done; - /* Add optional packet payload */ - if (addpacket && - imsg_add(obuf, pkt.pkt_buf, pkt.pkt_len) == -1) - goto done; + /* Add optional packet payload */ + if (pin->pin_buffer_id == OFP_PKTOUT_NO_BUFFER && + imsg_add(obuf, pkt.pkt_buf, pkt.pkt_len) == -1) + goto done; + } /* Set output header */ - memcpy(oh, ih, sizeof(*oh)); + oh->oh_version = OFP_V_1_3; oh->oh_length = htons(ibuf_length(obuf)); oh->oh_type = addflow ? OFP_T_FLOW_MOD : OFP_T_PACKET_OUT; oh->oh_xid = htonl(con->con_xidnxt++); @@ -527,9 +601,11 @@ ofp13_packet_in(struct switchd *sc, struct switch_connection *con, ofp_send(con, NULL, obuf); - if (addflow && addpacket) { + if (sendbuffer) { + ibuf_release(obuf); + /* loop to output the packet again */ - addflow = 0; + addflow = sendbuffer = 0; if ((obuf = ibuf_static()) == NULL) goto done; goto again; @@ -540,3 +616,866 @@ ofp13_packet_in(struct switchd *sc, struct switch_connection *con, ibuf_release(obuf); return (ret); } + +int +ofp13_flow_removed(struct switchd *sc, struct switch_connection *con, + struct ofp_header *ih, struct ibuf *ibuf) +{ + struct ofp_flow_removed *fr; + + if ((fr = ibuf_getdata(ibuf, sizeof(*fr))) == NULL) + return (-1); + + log_debug("%s: cookie:%llu priority:%d reason:%d tableid:%d " + "duration(%u sec, %u nsec) idleto:%d hard:%d packet:%llu byte:%llu", + __func__, be64toh(fr->fr_cookie), ntohs(fr->fr_priority), + fr->fr_reason, fr->fr_table_id, ntohl(fr->fr_duration_sec), + ntohl(fr->fr_duration_nsec), ntohs(fr->fr_idle_timeout), + ntohs(fr->fr_hard_timeout), + be64toh(fr->fr_packet_count), be64toh(fr->fr_byte_count)); + + switch (fr->fr_reason) { + case OFP_FLOWREM_REASON_IDLE_TIMEOUT: + log_debug("\tReason: IDLE TIMEOUT"); + break; + case OFP_FLOWREM_REASON_HARD_TIMEOUT: + log_debug("\tReason: HARD TIMEOUT"); + break; + case OFP_FLOWREM_REASON_DELETE: + log_debug("\tReason: DELETE"); + break; + case OFP_FLOWREM_REASON_GROUP_DELETE: + log_debug("\tReason: GROUP DELETE"); + break; + default: + log_debug("\tReason: UNKNOWN"); + break; + } + + return (0); +} + +/* + * The valid commands for groups are: + * OFP_GROUPCMD_{ADD,MODIFY,DELETE} + * + * The valid type for groups are: + * OFP_GROUP_T_{ALL,SELECT,INDIRECT,FAST_FAILOVER} + * + * You have to update the gm->gm_oh.oh_length = htons(ibuf_length(ibuf)); + */ +struct ofp_group_mod * +ofp13_group(struct switch_connection *con, struct ibuf *ibuf, + uint32_t gid, uint16_t cmd, uint8_t type) +{ + struct ofp_group_mod *gm; + struct ofp_header *oh; + + if ((gm = ibuf_advance(ibuf, sizeof(*gm))) == NULL) + return (NULL); + + oh = &gm->gm_oh; + oh->oh_version = OFP_V_1_3; + oh->oh_type = OFP_T_GROUP_MOD; + oh->oh_xid = htonl(con->con_xidnxt++); + gm->gm_command = htons(cmd); + gm->gm_type = type; + gm->gm_group_id = htonl(gid); + return (gm); +} + +/* Remember to update b->b_len. */ +struct ofp_bucket * +ofp13_bucket(struct ibuf *ibuf, uint16_t weight, uint32_t watchport, + uint32_t watchgroup) +{ + struct ofp_bucket *b; + + if ((b = ibuf_advance(ibuf, sizeof(*b))) == NULL) + return (NULL); + + b->b_weight = htons(weight); + b->b_watch_port = htonl(watchport); + b->b_watch_group = htonl(watchgroup); + return (b); +} + +/* Appends an action with just the generic header. */ +int +action_new(struct ibuf *ibuf, uint16_t type) +{ + struct ofp_action_header *ah; + + if ((ah = ibuf_advance(ibuf, sizeof(*ah))) == NULL) + return (-1); + + ah->ah_type = htons(type); + ah->ah_len = htons(sizeof(*ah)); + return (0); +} + +int +action_group(struct ibuf *ibuf, uint32_t group) +{ + struct ofp_action_group *ag; + + if ((ag = ibuf_advance(ibuf, sizeof(*ag))) == NULL) + return (-1); + + ag->ag_type = htons(OFP_ACTION_GROUP); + ag->ag_len = sizeof(*ag); + ag->ag_group_id = htonl(group); + return (0); +} + +int +action_output(struct ibuf *ibuf, uint32_t port, uint16_t maxlen) +{ + struct ofp_action_output *ao; + + if ((ao = ibuf_advance(ibuf, sizeof(*ao))) == NULL) + return (-1); + + ao->ao_type = htons(OFP_ACTION_OUTPUT); + ao->ao_len = htons(sizeof(*ao)); + ao->ao_port = htonl(port); + ao->ao_max_len = htons(maxlen); + return (0); +} + +/* + * This action pushes VLAN/MPLS/PBB tags into the outermost part of the + * packet. When the type is X ethertype must be Y: + * - OFP_ACTION_PUSH_VLAN: ETHERTYPE_VLAN or ETHERTYPE_QINQ. + * - OFP_ACTION_PUSH_MPLS: ETHERTYPE_MPLS or ETHERTYPE_MPLSCAST. + * - OFP_ACTION_PUSH_PBB: ETHERTYPE_??? (0x88E7). + */ +int +action_push(struct ibuf *ibuf, uint16_t type, uint16_t ethertype) +{ + struct ofp_action_push *ap; + + if ((ap = ibuf_advance(ibuf, sizeof(*ap))) == NULL) + return (-1); + + ap->ap_type = htons(type); + ap->ap_len = htons(sizeof(*ap)); + ap->ap_ethertype = htons(ethertype); + return (0); +} + +/* + * This action only pops the outermost VLAN tag and only one at a time, + * you can only pop multiple VLANs with an action list that is only + * availiable for OFP_INSTRUCTION_T_APPLY_ACTIONS. + */ +int +action_pop_vlan(struct ibuf *ibuf) +{ + return (action_new(ibuf, OFP_ACTION_POP_VLAN)); +} + +/* + * Use this with caution since this will pop MPLS shim regardless of the + * BoS bit state. + */ +int +action_pop_mpls(struct ibuf *ibuf, uint16_t ethertype) +{ + struct ofp_action_pop_mpls *apm; + + if ((apm = ibuf_advance(ibuf, sizeof(*apm))) == NULL) + return (-1); + + apm->apm_type = htons(OFP_ACTION_POP_MPLS); + apm->apm_len = htons(sizeof(*apm)); + apm->apm_ethertype = htons(ethertype); + return (0); +} + +int +action_copyttlout(struct ibuf *ibuf) +{ + return (action_new(ibuf, OFP_ACTION_COPY_TTL_OUT)); +} + +int +action_copyttlin(struct ibuf *ibuf) +{ + return (action_new(ibuf, OFP_ACTION_COPY_TTL_IN)); +} + +int +action_decnwttl(struct ibuf *ibuf) +{ + return (action_new(ibuf, OFP_ACTION_DEC_NW_TTL)); +} + +/* + * This function should be used with the oxm_*() family. + * + * After filling the action_setfield() with oxms you have to set the + * asf_len with htons(size_of_oxms). + */ +struct ofp_action_set_field * +action_setfield(struct ibuf *ibuf) +{ + struct ofp_action_set_field *asf; + + if ((asf = ibuf_advance(ibuf, sizeof(*asf))) == NULL) + return (NULL); + + asf->asf_type = htons(OFP_ACTION_SET_FIELD); + return (asf); +} + +struct ofp_ox_match * +oxm_get(struct ibuf *ibuf, uint16_t field, int hasmask, uint8_t len) +{ + struct ofp_ox_match *oxm; + size_t oxmlen; + + /* + * When the mask is used we must always reserve double the space, + * because the mask field is the same size of the value. + */ + if (hasmask) + len = len * 2; + + oxmlen = sizeof(*oxm) + len; + if ((oxm = ibuf_advance(ibuf, oxmlen)) == NULL) + return (NULL); + + oxm->oxm_class = htons(OFP_OXM_C_OPENFLOW_BASIC); + oxm->oxm_length = len; + OFP_OXM_SET_FIELD(oxm, field); + if (hasmask) + OFP_OXM_SET_HASMASK(oxm); + + return (oxm); +} + +/* + * OpenFlow port where the packet where received. + * May be a physical port, a logical port or the reserved port OFPP_LOCAL. + */ +int +oxm_inport(struct ibuf *ibuf, uint32_t in_port) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_IN_PORT, 0, + sizeof(in_port))) == NULL) + return (-1); + + in_port = htonl(in_port); + memcpy(oxm->oxm_value, &in_port, sizeof(in_port)); + return (0); +} + +/* + * Physical port on which the packet was received. + * Requires: oxm_inport. + */ +int +oxm_inphyport(struct ibuf *ibuf, uint32_t in_phy_port) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_IN_PHY_PORT, 0, + sizeof(in_phy_port))) == NULL) + return (-1); + + in_phy_port = htonl(in_phy_port); + memcpy(oxm->oxm_value, &in_phy_port, sizeof(in_phy_port)); + return (0); +} + +/* Table metadata. */ +int +oxm_metadata(struct ibuf *ibuf, int hasmask, uint64_t metadata, uint64_t mask) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_META, hasmask, + sizeof(metadata))) == NULL) + return (-1); + + metadata = htobe64(metadata); + memcpy(oxm->oxm_value, &metadata, sizeof(metadata)); + if (hasmask) { + mask = htobe64(mask); + memcpy(oxm->oxm_value + sizeof(metadata), &mask, sizeof(mask)); + } + + return (0); +} + +int +oxm_etheraddr(struct ibuf *ibuf, int issrc, uint8_t *addr, uint8_t *mask) +{ + struct ofp_ox_match *oxm; + int type; + int hasmask = (mask != NULL); + + type = issrc ? OFP_XM_T_ETH_SRC : OFP_XM_T_ETH_DST; + if ((oxm = oxm_get(ibuf, type, hasmask, ETHER_ADDR_LEN)) == NULL) + return (-1); + + memcpy(oxm->oxm_value, addr, ETHER_ADDR_LEN); + if (hasmask) + memcpy(oxm->oxm_value + ETHER_ADDR_LEN, mask, ETHER_ADDR_LEN); + + return (0); +} + +int +oxm_ethertype(struct ibuf *ibuf, uint16_t type) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_ETH_TYPE, 0, sizeof(type))) == NULL) + return (-1); + + type = htons(type); + memcpy(oxm->oxm_value, &type, sizeof(type)); + return (0); +} + +int +oxm_vlanvid(struct ibuf *ibuf, int hasmask, uint16_t vid, uint16_t mask) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_VLAN_VID, hasmask, + sizeof(vid))) == NULL) + return (-1); + + /* VID uses only the 13 least significant bits. */ + vid &= 0x1fff; + vid = htons(vid); + memcpy(oxm->oxm_value, &vid, sizeof(vid)); + if (hasmask) { + mask &= 0x1fff; + mask = htons(mask); + memcpy(oxm->oxm_value + sizeof(vid), &mask, sizeof(mask)); + } + + return (0); +} + +/* + * 802.1Q Prio from the outermost tag. + * + * Requires: oxm_vlanvid. + */ +int +oxm_vlanpcp(struct ibuf *ibuf, uint8_t pcp) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_VLAN_PCP, 0, sizeof(pcp))) == NULL) + return (-1); + + /* PCP only uses the lower 3 bits. */ + pcp &= 0x07; + memcpy(oxm->oxm_value, &pcp, sizeof(pcp)); + return (0); +} + +/* + * The Diff Serv Code Point (DSCP) bits avaliable in IPv4 ToS field or + * IPv6 Traffic Class field. + * + * Requires: oxm_ethertype(ETHERTYPE_IP) or oxm_ethertype(ETHERTYPE_IPV6). + */ +int +oxm_ipdscp(struct ibuf *ibuf, uint8_t dscp) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_IP_DSCP, 0, sizeof(dscp))) == NULL) + return (-1); + + /* Only the 6 lower bits have meaning. */ + dscp &= 0x3F; + memcpy(oxm->oxm_value, &dscp, sizeof(dscp)); + return (0); +} + +/* + * The ECN (Explicit Congestion Notification) bits of IP headers. + * + * Requires: oxm_ethertype(ETHERTYPE_IP) or oxm_ethertype(ETHERTYPE_IPV6). + */ +int +oxm_ipecn(struct ibuf *ibuf, uint8_t ecn) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_IP_ECN, 0, sizeof(ecn))) == NULL) + return (-1); + + /* Only the 2 most significant bits have meaning. */ + ecn &= 0x03; + memcpy(oxm->oxm_value, &ecn, sizeof(ecn)); + return (0); +} + +/* + * The IP protocol byte. + * + * Requires: oxm_ethertype(ETHERTYPE_IP) or oxm_ethertype(ETHERTYPE_IPV6). + */ +int +oxm_ipproto(struct ibuf *ibuf, uint8_t proto) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_IP_PROTO, 0, sizeof(proto))) == NULL) + return (-1); + + memcpy(oxm->oxm_value, &proto, sizeof(proto)); + return (0); +} + +/* + * The IPv4 address source/destination. + * + * Requires: oxm_ethertype(ETHERTYPE_IP). + */ +int +oxm_ipaddr(struct ibuf *ibuf, int issrc, int hasmask, uint32_t addr, + uint32_t mask) +{ + struct ofp_ox_match *oxm; + int type; + + type = issrc ? OFP_XM_T_IPV4_SRC : OFP_XM_T_IPV4_DST; + if ((oxm = oxm_get(ibuf, type, hasmask, sizeof(addr))) == NULL) + return (-1); + + addr = htonl(addr); + memcpy(oxm->oxm_value, &addr, sizeof(addr)); + if (hasmask) { + mask = htonl(mask); + memcpy(oxm->oxm_value + sizeof(addr), &mask, sizeof(mask)); + } + + return (0); +} + +/* + * The TCP source/destination port. + * + * Requirements: oxm_ethertype(ETHERTYPE_IP) or oxm_ethertype(ETHERTYPE_IPV6) + * and oxm_ipproto(IPPROTO_TCP). + */ +int +oxm_tcpport(struct ibuf *ibuf, int issrc, uint16_t port) +{ + struct ofp_ox_match *oxm; + int type; + + type = issrc ? OFP_XM_T_TCP_SRC : OFP_XM_T_TCP_DST; + if ((oxm = oxm_get(ibuf, type, 0, sizeof(port))) == NULL) + return (-1); + + port = htons(port); + memcpy(oxm->oxm_value, &port, sizeof(port)); + return (0); +} + +/* + * The UDP source/destination port. + * + * Requirements: oxm_ethertype(ETHERTYPE_IP) or oxm_ethertype(ETHERTYPE_IPV6) + * and oxm_ipproto(IPPROTO_UDP). + */ +int +oxm_udpport(struct ibuf *ibuf, int issrc, uint16_t port) +{ + struct ofp_ox_match *oxm; + int type; + + type = issrc ? OFP_XM_T_UDP_SRC : OFP_XM_T_UDP_DST; + if ((oxm = oxm_get(ibuf, type, 0, sizeof(port))) == NULL) + return (-1); + + port = htons(port); + memcpy(oxm->oxm_value, &port, sizeof(port)); + return (0); +} + +/* + * The SCTP source/destination port. + * + * Requirements: oxm_ethertype(ETHERTYPE_IP) or oxm_ethertype(ETHERTYPE_IPV6) + * and oxm_ipproto(IPPROTO_??? -- 132). + */ +int +oxm_sctpport(struct ibuf *ibuf, int issrc, uint16_t port) +{ + struct ofp_ox_match *oxm; + int type; + + type = issrc ? OFP_XM_T_SCTP_SRC : OFP_XM_T_SCTP_DST; + if ((oxm = oxm_get(ibuf, type, 0, sizeof(port))) == NULL) + return (-1); + + port = htons(port); + memcpy(oxm->oxm_value, &port, sizeof(port)); + return (0); +} + +/* + * The ICMPv4 type in the ICMP header. + * + * Requires: oxm_ethertype(ETHERTYPE_IP) and oxm_ipproto(IPPROTO_ICMP). + */ +int +oxm_icmpv4type(struct ibuf *ibuf, uint8_t type) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_ICMPV4_TYPE, 0, + sizeof(type))) == NULL) + return (-1); + + memcpy(oxm->oxm_value, &type, sizeof(type)); + return (0); +} + +/* + * The ICMPv4 code in the ICMP header. + * + * Requires: oxm_ethertype(ETHERTYPE_IP) and oxm_ipproto(IPPROTO_ICMP). + */ +int +oxm_icmpv4code(struct ibuf *ibuf, uint8_t code) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_ICMPV4_CODE, 0, + sizeof(code))) == NULL) + return (-1); + + memcpy(oxm->oxm_value, &code, sizeof(code)); + return (0); +} + +/* + * ARP opcode. + * + * Requires: oxm_ethertype(ETHERTYPE_ARP). + */ +int +oxm_arpop(struct ibuf *ibuf, uint16_t op) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_ARP_OP, 0, sizeof(op))) == NULL) + return (-1); + + op = htons(op); + memcpy(oxm->oxm_value, &op, sizeof(op)); + return (0); +} + +/* + * ARP source/target protocol address. + * + * Requires: oxm_ethertype(ETHERTYPE_ARP). + */ +int +oxm_arpaddr(struct ibuf *ibuf, int issrc, int hasmask, uint32_t addr, + uint32_t mask) +{ + struct ofp_ox_match *oxm; + int type; + + type = issrc ? OFP_XM_T_ARP_SPA : OFP_XM_T_ARP_TPA; + if ((oxm = oxm_get(ibuf, type, hasmask, sizeof(addr))) == NULL) + return (-1); + + addr = htonl(addr); + memcpy(oxm->oxm_value, &addr, sizeof(addr)); + if (hasmask) { + mask = htonl(mask); + memcpy(oxm->oxm_value + sizeof(addr), &mask, sizeof(mask)); + } + + return (0); +} + +/* + * ARP source/target hardware address. + * + * Requires: oxm_ethertype(ETHERTYPE_ARP). + */ +int +oxm_arphaddr(struct ibuf *ibuf, int issrc, uint8_t *addr, uint8_t *mask) +{ + struct ofp_ox_match *oxm; + int type; + int hasmask = (mask != NULL); + + type = issrc ? OFP_XM_T_ARP_SHA : OFP_XM_T_ARP_THA; + if ((oxm = oxm_get(ibuf, type, hasmask, ETHER_ADDR_LEN)) == NULL) + return (-1); + + memcpy(oxm->oxm_value, addr, ETHER_ADDR_LEN); + if (hasmask) + memcpy(oxm->oxm_value + ETHER_ADDR_LEN, mask, ETHER_ADDR_LEN); + + return (0); +} + +/* + * The source or destination of the IPv6 address. + * + * Requirements: oxm_ethertype(ETHERTYPE_IPV6). + */ +int +oxm_ipv6addr(struct ibuf *ibuf, int issrc, struct in6_addr *addr, + struct in6_addr *mask) +{ + struct ofp_ox_match *oxm; + int type; + int hasmask = (mask != NULL); + + type = issrc ? OFP_XM_T_IPV6_SRC : OFP_XM_T_IPV6_DST; + if ((oxm = oxm_get(ibuf, type, hasmask, sizeof(*addr))) == NULL) + return (-1); + + memcpy(oxm->oxm_value, addr, sizeof(*addr)); + if (hasmask) + memcpy(oxm->oxm_value + sizeof(*addr), mask, sizeof(*mask)); + + return (0); +} + +/* + * The IPv6 flow label field. + * + * Requirements: oxm_ethertype(ETHERTYPE_IPV6). + */ +int +oxm_ipv6flowlabel(struct ibuf *ibuf, int hasmask, uint32_t flowlabel, + uint32_t mask) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_IPV6_FLABEL, hasmask, + sizeof(flowlabel))) == NULL) + return (-1); + + /* + * 12 most significants bits forced to 0 and only the 20 lowers + * bits have meaning. + */ + flowlabel &= 0x000FFFFFU; + flowlabel = htonl(flowlabel); + memcpy(oxm->oxm_value, &flowlabel, sizeof(flowlabel)); + if (hasmask) { + mask &= 0x000FFFFFU; + mask = htonl(mask); + memcpy(oxm->oxm_value + sizeof(flowlabel), &mask, sizeof(mask)); + } + + return (0); +} + +/* + * The ICMPv6 type in ICMP header. + * + * Requirements: oxm_ethertype(ETHERTYPE_IPV6) and oxm_ipproto(IPPROTO_ICMPV6). + */ +int +oxm_icmpv6type(struct ibuf *ibuf, uint8_t type) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_ICMPV6_TYPE, 0, + sizeof(type))) == NULL) + return (-1); + + memcpy(oxm->oxm_value, &type, sizeof(type)); + return (0); +} + +/* + * The ICMPv6 code in ICMP header. + * + * Requirements: oxm_ethertype(ETHERTYPE_IPV6) and oxm_ipproto(IPPROTO_ICMPV6). + */ +int +oxm_icmpv6code(struct ibuf *ibuf, uint8_t code) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_ICMPV6_CODE, 0, + sizeof(code))) == NULL) + return (-1); + + memcpy(oxm->oxm_value, &code, sizeof(code)); + return (0); +} + +/* + * The target address in neighbour discovery message. + * + * Requirements: oxm_ethertype(ETHERTYPE_IPV6), oxm_ipproto(IPPROTO_ICMPV6) + * and oxm_icmpv6type(ND_NEIGHBOR_SOLICIT) or + * oxm_icmpv6type(ND_NEIGHBOR_ADVERT). + */ +int +oxm_ipv6ndtarget(struct ibuf *ibuf, struct in6_addr *addr) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_IPV6_ND_TARGET, 0, + sizeof(*addr))) == NULL) + return (-1); + + memcpy(oxm->oxm_value, addr, sizeof(*addr)); + return (0); +} + +/* + * The source link-layer address in an IPv6 Neighbour discovery. + * + * Requirements: oxm_ethertype(ETHERTYPE_IPV6), oxm_ipproto(IPPROTO_ICMPV6) + * and oxm_icmpv6type(ND_NEIGHBOR_SOLICIT). + */ +int +oxm_ipv6ndlinkaddr(struct ibuf *ibuf, int issrc, uint8_t *addr) +{ + struct ofp_ox_match *oxm; + int type; + + type = issrc ? OFP_XM_T_IPV6_ND_SLL : OFP_XM_T_IPV6_ND_TLL; + if ((oxm = oxm_get(ibuf, type, 0, ETHER_ADDR_LEN)) == NULL) + return (-1); + + memcpy(oxm->oxm_value, addr, ETHER_ADDR_LEN); + return (0); +} + +/* + * The label in the MPLS shim. + * + * Requirements: oxm_ethertype(ETHERTYPE_MPLS) or + * oxm_ethertype(ETHERTYPE_MPLS_MCAST). + */ +int +oxm_mplslabel(struct ibuf *ibuf, uint32_t label) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_MPLS_LABEL, 0, + sizeof(label))) == NULL) + return (-1); + + label &= MPLS_LABEL_MASK; + label = htonl(label); + memcpy(oxm->oxm_value, &label, sizeof(label)); + return (0); +} + +/* + * The TC in the first MPLS shim. + * + * Requirements: oxm_ethertype(ETHERTYPE_MPLS) or + * oxm_ethertype(ETHERTYPE_MPLS_MCAST). + */ +int +oxm_mplstc(struct ibuf *ibuf, uint8_t tc) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_MPLS_TC, 0, sizeof(tc))) == NULL) + return (-1); + + tc &= 0x07; + memcpy(oxm->oxm_value, &tc, sizeof(tc)); + return (0); +} + +/* + * The BoS bit in the first MPLS shim. + * + * Requirements: oxm_ethertype(ETHERTYPE_MPLS) or + * oxm_ethertype(ETHERTYPE_MPLS_MCAST). + */ +int +oxm_mplsbos(struct ibuf *ibuf, uint8_t bos) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_MPLS_BOS, 0, sizeof(bos))) == NULL) + return (-1); + + bos &= 0x01; + memcpy(oxm->oxm_value, &bos, sizeof(bos)); + return (0); +} + +/* + * Comment shamelessly taken from OpenFlow 1.3.5 specification. + * + * Metadata associated with a logical port. + * + * If the logical port performs encapsulation and decapsulation, this + * is the demultiplexing field from the encapsulation header. + * For example, for a packet received via GRE tunnel including a (32-bit) key, + * the key is stored in the low 32-bits and the high bits are zeroed. + * For a MPLS logical port, the low 20 bits represent the MPLS Label. + * For a VxLAN logical port, the low 24 bits represent the VNI. + * If the packet is not received through a logical port, the value is 0. + */ +int +oxm_tunnelid(struct ibuf *ibuf, int hasmask, uint64_t id, uint64_t mask) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_TUNNEL_ID, hasmask, + sizeof(id))) == NULL) + return (-1); + + id = htobe64(id); + memcpy(oxm->oxm_value, &id, sizeof(id)); + if (hasmask) { + mask = htobe64(mask); + memcpy(oxm->oxm_value + sizeof(id), &mask, sizeof(mask)); + } + return (0); +} + +/* + * The IPv6 extension header. + * + * Tip: use the OFP_XM_IPV6_EXTHDR_* macros. + * + * Requirements: oxm_ethertype(ETHERTYPE_IPV6). + */ +int +oxm_ipv6exthdr(struct ibuf *ibuf, int hasmask, uint16_t exthdr, uint16_t mask) +{ + struct ofp_ox_match *oxm; + + if ((oxm = oxm_get(ibuf, OFP_XM_T_IPV6_EXTHDR, hasmask, + sizeof(exthdr))) == NULL) + return (-1); + + /* Only the lower 9 bits have meaning. */ + exthdr &= 0x01FF; + exthdr = htons(exthdr); + memcpy(oxm->oxm_value, &exthdr, sizeof(exthdr)); + if (hasmask) { + mask &= 0x01FF; + mask = htons(mask); + memcpy(oxm->oxm_value + sizeof(exthdr), &mask, sizeof(mask)); + } + return (0); +} |