summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorRafael Zalamena <rzalamena@cvs.openbsd.org>2016-09-26 12:33:05 +0000
committerRafael Zalamena <rzalamena@cvs.openbsd.org>2016-09-26 12:33:05 +0000
commitc7bcee12fae570d16ba3ba268157a443400e2310 (patch)
tree8a5d56e937abcf3c8262b1c7af60feeed21af5db /usr.sbin
parent940ab56dc3e3ac584c55e3e14dc1e7de4d7b2351 (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.h129
-rw-r--r--usr.sbin/switchd/ofp13.c1027
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);
+}