@@ -1,5 +1,7 @@
Post-v2.5.0
---------------------
+ - OpenFlow:
+ * New "arp" OpenFlow extension action for generating ARP packets.
v2.5.0 - xx xxx xxxx
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2014 Nicira, Inc.
+ * Copyright (c) 2007-2015 Nicira, Inc.
*
* This file is offered under your choice of two licenses: Apache 2.0 or GNU
* GPL 2.0 or later. The permission statements for each of these licenses is
@@ -783,6 +783,8 @@ enum ovs_nat_attr {
* ovs_action_push_tnl.
* @OVS_ACTION_ATTR_TUNNEL_POP: Lookup tunnel port by port-no passed and pop
* tunnel header.
+ * @OVS_ACTION_ATTR_ARP: Transform IPv4 packet into ARP packet, execute nested
+ * actions, restore IPv4 packet.
*/
enum ovs_action_attr {
@@ -806,6 +808,7 @@ enum ovs_action_attr {
#ifndef __KERNEL__
OVS_ACTION_ATTR_TUNNEL_PUSH, /* struct ovs_action_push_tnl*/
OVS_ACTION_ATTR_TUNNEL_POP, /* u32 port number. */
+ OVS_ACTION_ATTR_ARP, /* Nested OVS_ACTION_ATTR_*. */
#endif
__OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted
* from userspace. */
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -3627,6 +3627,7 @@ dp_execute_cb(void *aux_, struct dp_packet **packets, int cnt,
case OVS_ACTION_ATTR_SET_MASKED:
case OVS_ACTION_ATTR_SAMPLE:
case OVS_ACTION_ATTR_HASH:
+ case OVS_ACTION_ATTR_ARP:
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
OVS_NOT_REACHED();
@@ -1146,6 +1146,7 @@ dpif_execute_helper_cb(void *aux_, struct dp_packet **packets, int cnt,
case OVS_ACTION_ATTR_SET:
case OVS_ACTION_ATTR_SET_MASKED:
case OVS_ACTION_ATTR_SAMPLE:
+ case OVS_ACTION_ATTR_ARP:
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
OVS_NOT_REACHED();
@@ -34,6 +34,8 @@
#include "unaligned.h"
#include "util.h"
+#include "openvswitch/vlog.h"
+
/* Masked copy of an ethernet address. 'src' is already properly masked. */
static void
ether_addr_copy_masked(struct eth_addr *dst, const struct eth_addr src,
@@ -480,6 +482,23 @@ odp_execute_sample(void *dp, struct dp_packet *packet, bool steal,
nl_attr_get_size(subactions), dp_execute_action);
}
+static void
+odp_execute_arp(void *dp, struct dp_packet *ip,
+ const struct nlattr *action, odp_execute_cb dp_execute_action)
+{
+ uint64_t stub[DIV_ROUND_UP(2 + ETH_HEADER_LEN + VLAN_HEADER_LEN +
+ ARP_ETH_HEADER_LEN, 8)];
+ struct dp_packet arp;
+ dp_packet_use_stub(&arp, stub, sizeof stub);
+ compose_arp__(&arp);
+ arp.md = ip->md;
+
+ struct dp_packet *arpp = &arp;
+ odp_execute_actions(dp, &arpp, 1, false, nl_attr_get(action),
+ nl_attr_get_size(action), dp_execute_action);
+ dp_packet_uninit(&arp);
+}
+
static bool
requires_datapath_assistance(const struct nlattr *a)
{
@@ -503,6 +522,7 @@ requires_datapath_assistance(const struct nlattr *a)
case OVS_ACTION_ATTR_HASH:
case OVS_ACTION_ATTR_PUSH_MPLS:
case OVS_ACTION_ATTR_POP_MPLS:
+ case OVS_ACTION_ATTR_ARP:
return false;
case OVS_ACTION_ATTR_UNSPEC:
@@ -623,6 +643,12 @@ odp_execute_actions(void *dp, struct dp_packet **packets, int cnt, bool steal,
}
break;
+ case OVS_ACTION_ATTR_ARP:
+ for (i = 0; i < cnt; i++) {
+ odp_execute_arp(dp, packets[i], a, dp_execute_action);
+ }
+ break;
+
case OVS_ACTION_ATTR_OUTPUT:
case OVS_ACTION_ATTR_TUNNEL_PUSH:
case OVS_ACTION_ATTR_TUNNEL_POP:
@@ -120,6 +120,7 @@ odp_action_len(uint16_t type)
case OVS_ACTION_ATTR_SET_MASKED: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_SAMPLE: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_CT: return ATTR_LEN_VARIABLE;
+ case OVS_ACTION_ATTR_ARP: return ATTR_LEN_VARIABLE;
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
@@ -766,6 +767,14 @@ format_odp_conntrack_action(struct ds *ds, const struct nlattr *attr)
}
static void
+format_odp_arp_action(struct ds *ds, const struct nlattr *attr)
+{
+ ds_put_cstr(ds, "arp(");
+ format_odp_actions(ds, nl_attr_get(attr), nl_attr_get_size(attr));
+ ds_put_char(ds, ')');
+}
+
+static void
format_odp_action(struct ds *ds, const struct nlattr *a)
{
int expected_len;
@@ -858,6 +867,9 @@ format_odp_action(struct ds *ds, const struct nlattr *a)
case OVS_ACTION_ATTR_CT:
format_odp_conntrack_action(ds, a);
break;
+ case OVS_ACTION_ATTR_ARP:
+ format_odp_arp_action(ds, a);
+ break;
case OVS_ACTION_ATTR_UNSPEC:
case __OVS_ACTION_ATTR_MAX:
default:
@@ -295,6 +295,9 @@ enum ofp_raw_action_type {
/* NX1.0+(36): struct nx_action_nat, ... */
NXAST_RAW_NAT,
+ /* NX1.0+(37): struct nx_action_arp, ... */
+ NXAST_RAW_ARP,
+
/* ## ------------------ ## */
/* ## Debugging actions. ## */
/* ## ------------------ ## */
@@ -423,6 +426,9 @@ ofpact_next_flattened(const struct ofpact *ofpact)
case OFPACT_CT:
return ofpact_get_CT(ofpact)->actions;
+ case OFPACT_ARP:
+ return ofpact_get_ARP(ofpact)->actions;
+
case OFPACT_WRITE_ACTIONS:
return ofpact_get_WRITE_ACTIONS(ofpact)->actions;
}
@@ -5349,7 +5355,108 @@ parse_NAT(char *arg, struct ofpbuf *ofpacts,
}
return NULL;
}
+
+/* Action structure for NXAST_ARP.
+ *
+ * The only content is a series of nested OpenFlow actions to act on the ARP
+ * packet.
+ */
+struct nx_action_arp {
+ ovs_be16 type; /* OFPAT_VENDOR. */
+ ovs_be16 len; /* At least 16. */
+ ovs_be32 vendor; /* NX_VENDOR_ID. */
+ ovs_be16 subtype; /* NXAST_ARP. */
+ uint8_t zero[6]; /* Must be zero. */
+ /* Followed by a sequence of zero or more OpenFlow actions. The length of
+ * these is included in 'len'. */
+};
+OFP_ASSERT(sizeof(struct nx_action_arp) == 16);
+
+static enum ofperr
+decode_NXAST_RAW_ARP(const struct nx_action_arp *naa,
+ enum ofp_version ofp_version, struct ofpbuf *out)
+{
+ const size_t arp_offset = ofpacts_pull(out);
+ struct ofpact_nest *arp;
+ struct ofpbuf openflow;
+ int error = 0;
+
+ if (!is_all_zeros(naa->zero, sizeof naa->zero)) {
+ return OFPERR_NXBAC_MUST_BE_ZERO;
+ }
+
+ arp = ofpact_put_ARP(out);
+ ofpbuf_pull(out, sizeof *arp);
+
+ ofpbuf_use_const(&openflow, naa + 1, ntohs(naa->len) - sizeof *naa);
+ error = ofpacts_pull_openflow_actions__(
+ &openflow, openflow.size, ofp_version,
+ 1u << OVSINST_OFPIT11_APPLY_ACTIONS, out, OFPACT_ARP);
+ if (error) {
+ goto out;
+ }
+
+ arp = ofpbuf_push_uninit(out, sizeof *arp);
+ out->header = &arp->ofpact;
+ ofpact_update_len(out, &arp->ofpact);
+
+out:
+ ofpbuf_push_uninit(out, arp_offset);
+ return error;
+}
+
+static void
+encode_ARP(const struct ofpact_nest *arp, enum ofp_version ofp_version,
+ struct ofpbuf *out)
+{
+ struct nx_action_arp *naa;
+ const size_t ofs = out->size;
+ size_t len;
+
+ naa = put_NXAST_ARP(out);
+
+ len = ofpacts_put_openflow_actions(arp->actions,
+ ofpact_nest_get_action_len(arp),
+ out, ofp_version);
+ len += sizeof *naa;
+ naa = ofpbuf_at(out, ofs, sizeof *naa);
+ naa->len = htons(len);
+}
+
+/* Parses 'arg' as the argument to a "arp" action, and appends such an
+ * action to 'ofpacts'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+static char * OVS_WARN_UNUSED_RESULT
+parse_ARP(char *arg, struct ofpbuf *ofpacts,
+ enum ofputil_protocol *usable_protocols)
+{
+ const size_t arp_offset = ofpacts_pull(ofpacts);
+ struct ofpact_nest *arp = ofpact_put_ARP(ofpacts);
+
+ /* Hide existing actions from ofpacts_parse_copy(), so the
+ * nesting can be handled transparently. */
+ ofpbuf_pull(ofpacts, sizeof *arp);
+ char *error = ofpacts_parse_copy(arg, ofpacts, usable_protocols, false,
+ OFPACT_ARP);
+ ofpact_pad(ofpacts);
+ ofpacts->header = ofpbuf_push_uninit(ofpacts, sizeof *arp);
+ arp = ofpacts->header;
+
+ ofpact_update_len(ofpacts, &arp->ofpact);
+ ofpbuf_push_uninit(ofpacts, arp_offset);
+
+ return error;
+}
+static void
+format_ARP(const struct ofpact_nest *a, struct ds *s)
+{
+ ds_put_cstr(s, "arp(");
+ ofpacts_format(a->actions, ofpact_nest_get_action_len(a), s);
+ ds_put_char(s, ')');
+}
/* Meter instruction. */
@@ -5749,6 +5856,7 @@ ofpact_is_set_or_move_action(const struct ofpact *a)
case OFPACT_OUTPUT:
case OFPACT_OUTPUT_REG:
case OFPACT_POP_MPLS:
+ case OFPACT_ARP:
case OFPACT_POP_QUEUE:
case OFPACT_PUSH_MPLS:
case OFPACT_PUSH_VLAN:
@@ -5822,6 +5930,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a)
case OFPACT_SAMPLE:
case OFPACT_STACK_POP:
case OFPACT_STACK_PUSH:
+ case OFPACT_ARP:
case OFPACT_DEBUG_RECIRC:
/* The action set may only include actions and thus
@@ -6024,6 +6133,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type type)
case OFPACT_DEC_MPLS_TTL:
case OFPACT_PUSH_MPLS:
case OFPACT_POP_MPLS:
+ case OFPACT_ARP:
case OFPACT_SET_TUNNEL:
case OFPACT_SET_QUEUE:
case OFPACT_POP_QUEUE:
@@ -6592,6 +6702,21 @@ ofpact_check__(enum ofputil_protocol *usable_protocols, struct ofpact *a,
flow->dl_type = ofpact_get_POP_MPLS(a)->ethertype;
return 0;
+ case OFPACT_ARP: {
+ if (flow->dl_type != htons(ETH_TYPE_IP)) {
+ return OFPERR_NXBAC_ARP_REQUIRES_IPV4;
+ }
+
+ flow->dl_type = htons(ETH_TYPE_ARP);
+ struct ofpact_nest *oa = ofpact_get_ARP(a);
+ enum ofperr error = ofpacts_check(oa->actions,
+ ofpact_nest_get_action_len(oa),
+ flow, max_ports, table_id, n_tables,
+ usable_protocols);
+ flow->dl_type = htons(ETH_TYPE_IP);
+ return error;
+ }
+
case OFPACT_SAMPLE:
return 0;
@@ -6784,7 +6909,8 @@ ofpacts_verify_nested(const struct ofpact *a, enum ofpact_type outer_action)
if (outer_action) {
ovs_assert(outer_action == OFPACT_WRITE_ACTIONS
- || outer_action == OFPACT_CT);
+ || outer_action == OFPACT_CT
+ || outer_action == OFPACT_ARP);
if (outer_action == OFPACT_CT) {
if (!field) {
@@ -7130,6 +7256,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, ofp_port_t port)
case OFPACT_SET_MPLS_TC:
case OFPACT_SET_MPLS_TTL:
case OFPACT_DEC_MPLS_TTL:
+ case OFPACT_ARP:
case OFPACT_SET_TUNNEL:
case OFPACT_WRITE_METADATA:
case OFPACT_SET_QUEUE:
@@ -86,6 +86,7 @@
OFPACT(DEC_MPLS_TTL, ofpact_null, ofpact, "dec_mpls_ttl") \
OFPACT(PUSH_MPLS, ofpact_push_mpls, ofpact, "push_mpls") \
OFPACT(POP_MPLS, ofpact_pop_mpls, ofpact, "pop_mpls") \
+ OFPACT(ARP, ofpact_nest, ofpact, "arp") \
\
/* Metadata. */ \
OFPACT(SET_TUNNEL, ofpact_tunnel, ofpact, "set_tunnel") \
@@ -475,7 +476,7 @@ struct ofpact_meter {
/* OFPACT_WRITE_ACTIONS.
*
- * Used for OFPIT11_WRITE_ACTIONS. */
+ * Used for OFPIT11_WRITE_ACTIONS and NXAST_ARP. */
struct ofpact_nest {
struct ofpact ofpact;
uint8_t pad[PAD_SIZE(sizeof(struct ofpact), OFPACT_ALIGNTO)];
@@ -269,6 +269,10 @@ enum ofperr {
* 64. */
OFPERR_NXBAC_BAD_CONJUNCTION,
+ /* NX1.0-1.1(2,527), NX1.2+(23). ARP is allowed only in a flow that
+ * matches on Ethertype 0x800. */
+ OFPERR_NXBAC_ARP_REQUIRES_IPV4,
+
/* ## --------------------- ## */
/* ## OFPET_BAD_INSTRUCTION ## */
/* ## --------------------- ## */
@@ -1191,35 +1191,45 @@ packet_format_tcp_flags(struct ds *s, uint16_t tcp_flags)
* 'arp_op', 'arp_sha', 'arp_tha', 'arp_spa', and 'arp_tpa'. The outer
* Ethernet frame is initialized with Ethernet source 'arp_sha' and destination
* 'arp_tha', except that destination ff:ff:ff:ff:ff:ff is used instead if
- * 'broadcast' is true. */
+ * 'broadcast' is true. Points the L3 header to the ARP header. */
void
compose_arp(struct dp_packet *b, uint16_t arp_op,
const struct eth_addr arp_sha, const struct eth_addr arp_tha,
bool broadcast, ovs_be32 arp_spa, ovs_be32 arp_tpa)
{
- struct eth_header *eth;
- struct arp_eth_header *arp;
+ compose_arp__(b);
+
+ struct eth_header *eth = dp_packet_l2(b);
+ eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha;
+ eth->eth_src = arp_sha;
+
+ struct arp_eth_header *arp = dp_packet_l3(b);
+ arp->ar_op = htons(arp_op);
+ arp->ar_sha = arp_sha;
+ arp->ar_tha = arp_tha;
+ put_16aligned_be32(&arp->ar_spa, arp_spa);
+ put_16aligned_be32(&arp->ar_tpa, arp_tpa);
+}
+/* Clears 'b' and replaces its contents by an ARP frame. Sets the fields in
+ * the Ethernet and ARP headers that are fixed for ARP frames to those fixed
+ * values, and zeroes the other fields. Points the L3 header to the ARP
+ * header. */
+void
+compose_arp__(struct dp_packet *b)
+{
dp_packet_clear(b);
dp_packet_prealloc_tailroom(b, ARP_PACKET_SIZE);
dp_packet_reserve(b, 2 + VLAN_HEADER_LEN);
- eth = dp_packet_put_uninit(b, sizeof *eth);
- eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha;
- eth->eth_src = arp_sha;
+ struct eth_header *eth = dp_packet_put_zeros(b, sizeof *eth);
eth->eth_type = htons(ETH_TYPE_ARP);
- arp = dp_packet_put_uninit(b, sizeof *arp);
+ struct arp_eth_header *arp = dp_packet_put_zeros(b, sizeof *arp);
arp->ar_hrd = htons(ARP_HRD_ETHERNET);
arp->ar_pro = htons(ARP_PRO_IP);
arp->ar_hln = sizeof arp->ar_sha;
arp->ar_pln = sizeof arp->ar_spa;
- arp->ar_op = htons(arp_op);
- arp->ar_sha = arp_sha;
- arp->ar_tha = arp_tha;
-
- put_16aligned_be32(&arp->ar_spa, arp_spa);
- put_16aligned_be32(&arp->ar_tpa, arp_tpa);
dp_packet_reset_offsets(b);
dp_packet_set_l3(b, arp);
@@ -1041,6 +1041,7 @@ void packet_set_nd(struct dp_packet *, const ovs_be32 target[4],
void packet_format_tcp_flags(struct ds *, uint16_t);
const char *packet_tcp_flag_to_string(uint32_t flag);
+void compose_arp__(struct dp_packet *);
void compose_arp(struct dp_packet *, uint16_t arp_op,
const struct eth_addr arp_sha,
const struct eth_addr arp_tha, bool broadcast,
@@ -1138,6 +1138,10 @@ dpif_sflow_read_actions(const struct flow *flow,
}
break;
+ case OVS_ACTION_ATTR_ARP:
+ /* XXX Do not handle this for now. */
+ break;
+
case OVS_ACTION_ATTR_USERSPACE:
case OVS_ACTION_ATTR_RECIRC:
case OVS_ACTION_ATTR_HASH:
@@ -4163,6 +4163,7 @@ recirc_unroll_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
case OFPACT_CONTROLLER:
case OFPACT_DEC_MPLS_TTL:
case OFPACT_DEC_TTL:
+ case OFPACT_ARP:
recirc_put_unroll_xlate(ctx);
break;
@@ -4387,6 +4388,71 @@ compose_conntrack_action(struct xlate_ctx *ctx, struct ofpact_conntrack *ofc)
}
static void
+execute_arp_action(struct xlate_ctx *ctx, const struct ofpact_nest *arp)
+{
+ ctx->xout->slow |= SLOW_ACTION;
+
+ /* Commit the current flow and save it.
+ *
+ * (We don't save ctx->base_flow separately because the commit should
+ * ensure that it is identical.) */
+ xlate_commit_actions(ctx);
+ struct flow old_flow = ctx->xin->flow;
+
+ /* Transform ctx->in->flow to ARP. */
+ ctx->xin->flow.dl_type = htons(ETH_TYPE_ARP);
+ ctx->xin->flow.nw_proto = ARP_OP_REQUEST;
+ ctx->xin->flow.arp_sha = ctx->xin->flow.dl_src;
+ ctx->xin->flow.arp_tha = ctx->xin->flow.dl_dst;
+
+ /* This introduces a read dependency on dl_src and dl_dst, so mark them in
+ * the wildcard mask. We also have to mark the ARP fields; otherwise at
+ * commit time we won't even consider them. */
+ memset(&ctx->wc->masks.dl_src, 0xff, sizeof ctx->wc->masks.dl_src);
+ memset(&ctx->wc->masks.dl_dst, 0xff, sizeof ctx->wc->masks.dl_dst);
+ memset(&ctx->wc->masks.nw_src, 0xff, sizeof ctx->wc->masks.nw_src);
+ memset(&ctx->wc->masks.nw_dst, 0xff, sizeof ctx->wc->masks.nw_dst);
+ memset(&ctx->wc->masks.nw_proto, 0xff, sizeof ctx->wc->masks.nw_proto);
+ memset(&ctx->wc->masks.arp_sha, 0xff, sizeof ctx->wc->masks.arp_sha);
+ memset(&ctx->wc->masks.arp_tha, 0xff, sizeof ctx->wc->masks.arp_tha);
+
+ /* Transform ctx->base_flow to ARP. This differs from ctx->xin->flow
+ * because the datapath ARP action zeroes all field values. */
+ ctx->base_flow = ctx->xin->flow;
+ ctx->base_flow.vlan_tci = 0;
+ ctx->base_flow.dl_src = eth_addr_zero;
+ ctx->base_flow.dl_dst = eth_addr_zero;
+ ctx->base_flow.nw_src = 0; /* arp_spa */
+ ctx->base_flow.nw_dst = 0; /* arp_tpa */
+ ctx->base_flow.arp_sha = eth_addr_zero;
+ ctx->base_flow.arp_tha = eth_addr_zero;
+
+ /* Save ctx->xin->packet. Then, if it was nonnull, replace it by a
+ * synthesized ARP packet. */
+ const struct dp_packet *old_packet = ctx->xin->packet;
+ uint64_t stub[DIV_ROUND_UP(2 + ETH_HEADER_LEN + VLAN_HEADER_LEN +
+ ARP_ETH_HEADER_LEN, 8)];
+ struct dp_packet packet;
+ if (old_packet) {
+ dp_packet_use_stub(&packet, stub, sizeof stub);
+ compose_arp__(&packet);
+ packet.md = old_packet->md;
+ ctx->xin->packet = &packet;
+ }
+
+ size_t start = nl_msg_start_nested(ctx->odp_actions, OVS_ACTION_ATTR_ARP);
+ do_xlate_actions(arp->actions, ofpact_nest_get_action_len(arp), ctx);
+ nl_msg_end_nested(ctx->odp_actions, start);
+
+ /* Restore old flow and base_flow. Restore old packet. */
+ ctx->base_flow = ctx->xin->flow = old_flow;
+ if (old_packet) {
+ dp_packet_uninit(&packet);
+ ctx->xin->packet = old_packet;
+ }
+}
+
+static void
do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
struct xlate_ctx *ctx)
{
@@ -4634,6 +4700,10 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
compose_mpls_pop_action(ctx, ofpact_get_POP_MPLS(a)->ethertype);
break;
+ case OFPACT_ARP:
+ execute_arp_action(ctx, ofpact_get_ARP(a));
+ break;
+
case OFPACT_SET_MPLS_LABEL:
CHECK_MPLS_RECIRCULATION();
compose_set_mpls_label_action(
@@ -3044,6 +3044,71 @@ arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=ff:ff:ff:ff:ff:f
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - IP to ARP transformation slow-path])
+OVS_VSWITCHD_START
+
+# Add pairs of ports for input and output.
+ADD_OF_PORTS([br0], [1], [2], [3], [4], [11], [12], [13], [14])
+for i in 11 12 13 14; do
+ ovs-vsctl -- set Interface p$i type=dummy options:pcap=p$i.pcap
+done
+
+# Add flows that do various kind of "arp" action transformations
+# from port 1 to 11, 2 to 12, 3 to 13, 4 to 14.
+AT_DATA([flows.txt], [dnl
+in_port=1,ip actions=arp(11),arp(set_field:2->arp_op,11),11
+in_port=2,ip actions=mod_vlan_vid:123,arp(12),arp(set_field:2->arp_op,12),12
+in_port=3,ip actions=arp(mod_vlan_vid:123,13),arp(set_field:2->arp_op,13),13
+in_port=4,ip actions=arp(14),strip_vlan,arp(set_field:2->arp_op,14),14
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+# Input two packets (one without a VLAN, one with VLAN 99) on each input port.
+for i in 1 2 3 4; do
+ ovs-appctl netdev-dummy/receive p$i "in_port($i),eth(src=80:88:88:88:88:88,dst=10:20:30:40:50:60),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=8,dst=10)"
+ ovs-appctl netdev-dummy/receive p$i "in_port($i),eth(src=80:88:88:88:88:88,dst=10:20:30:40:50:60),eth_type(0x8100),vlan(vid=99,pcp=7),encap(eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.1.2,proto=17,tos=0,ttl=64,frag=no),udp(src=8,dst=10))"
+done
+AT_CHECK([ovs-appctl time/warp 5000], [0], [ignore])
+
+# Check the packets that were output.
+AT_CHECK([ovs-ofctl parse-pcap p11.pcap], [0], [dnl
+arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+udp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10
+arp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+arp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+udp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10
+])
+
+AT_CHECK([ovs-ofctl parse-pcap p12.pcap], [0], [dnl
+arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=0,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=0,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+udp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=0,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10
+arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+udp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10
+])
+
+AT_CHECK([ovs-ofctl parse-pcap p13.pcap], [0], [dnl
+arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=0,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+udp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10
+arp,in_port=ANY,dl_vlan=123,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+arp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+udp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10
+])
+
+AT_CHECK([ovs-ofctl parse-pcap p14.pcap], [0], [dnl
+arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+udp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10
+arp,in_port=ANY,dl_vlan=99,dl_vlan_pcp=7,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=1,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+arp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,arp_spa=192.168.0.1,arp_tpa=192.168.1.2,arp_op=2,arp_sha=80:88:88:88:88:88,arp_tha=10:20:30:40:50:60
+udp,in_port=ANY,vlan_tci=0x0000,dl_src=80:88:88:88:88:88,dl_dst=10:20:30:40:50:60,nw_src=192.168.0.1,nw_dst=192.168.1.2,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=8,tp_dst=10
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto-dpif - VLAN handling])
OVS_VSWITCHD_START(
[set Bridge br0 fail-mode=standalone -- \
@@ -1740,6 +1740,32 @@ nf_conntrack module loaded.
.
.RE
.
+.IP \fBarp(\fIaction\fR...\fB)\fR
+Transforms an IPv4 packet into an ARP packet, then executes each
+nested \fIaction\fR on the ARP packet. Actions following the
+\fBarp\fR action apply to the original packet, not the ARP packet,
+discarding any changes that the nested actions made to packet data or
+metadata.
+.
+.IP
+The initial values of fields in the ARP packet are derived as follows:
+.RS
+.IP "\fBdl_src\fR and \fBdl_dst\fR"
+Unchanged from the IPv4 packet.
+.IP "\fBdl_type\fR"
+0x0806 (ARP)
+.IP "\fBnw_proto\fR (ARP opcode)"
+1 (ARP request)
+.IP "\fBarp_sha\fR"
+Copied from the Ethernet source address.
+.IP "\fBarp_spa\fR"
+Copied from the IP source address.
+.IP "\fBarp_tha\fR"
+Copied from the Ethernet destination address.
+.IP "\fBarp_tpa\fR"
+Copied from the IP destination address.
+.RE
+.
.IP \fBdec_ttl\fR
.IQ \fBdec_ttl(\fIid1\fR[\fB,\fIid2\fR]...\fB)\fR
Decrement TTL of IPv4 packet or hop limit of IPv6 packet. If the
This is useful for generating an ARP request from the OpenFlow flow table, which in turn is useful for implementing a router. An upcoming commit will use this feature to implement ARP request generationg in the OVN L3 logical router. Signed-off-by: Ben Pfaff <blp@ovn.org> --- NEWS | 2 + datapath/linux/compat/include/linux/openvswitch.h | 5 +- lib/dpif-netdev.c | 3 +- lib/dpif.c | 1 + lib/odp-execute.c | 26 +++++ lib/odp-util.c | 12 ++ lib/ofp-actions.c | 129 +++++++++++++++++++++- lib/ofp-actions.h | 3 +- lib/ofp-errors.h | 4 + lib/packets.c | 36 +++--- lib/packets.h | 1 + ofproto/ofproto-dpif-sflow.c | 4 + ofproto/ofproto-dpif-xlate.c | 70 ++++++++++++ tests/ofproto-dpif.at | 65 +++++++++++ utilities/ovs-ofctl.8.in | 26 +++++ 15 files changed, 370 insertions(+), 17 deletions(-)