@@ -591,16 +591,29 @@ consider_neighbor_flow(struct hmap *flow_table,
return;
}
- ovs_be32 ip;
- if (!ip_parse(b->ip, &ip)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
- return;
+
+ if (strchr(b->ip, '.')) {
+ ovs_be32 ip;
+ if (!ip_parse(b->ip, &ip)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
+ return;
+ }
+ match_set_reg(match_p, 0, ntohl(ip));
+ } else {
+ struct in6_addr ip6;
+ if (!ipv6_parse(b->ip, &ip6)) {
+ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+ VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
+ return;
+ }
+ ovs_be128 value;
+ memcpy(&value, &ip6, sizeof(value));
+ match_set_xxreg(match_p, 0, ntoh128(value));
}
match_set_metadata(match_p, htonll(pb->datapath->tunnel_key));
match_set_reg(match_p, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key);
- match_set_reg(match_p, 0, ntohl(ip));
ofpbuf_clear(ofpacts_p);
put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, ofpacts_p);
@@ -53,8 +53,9 @@ static struct rconn *swconn;
* rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */
static unsigned int conn_seq_no;
-static void pinctrl_handle_put_arp(const struct flow *md,
- const struct flow *headers);
+static void pinctrl_handle_put_mac_binding(const struct flow *md,
+ const struct flow *headers,
+ bool is_ipv4);
static void init_put_arps(void);
static void destroy_put_arps(void);
static void run_put_arps(struct controller_ctx *,
@@ -402,7 +403,8 @@ process_packet_in(const struct ofp_header *msg)
break;
case ACTION_OPCODE_PUT_ARP:
- pinctrl_handle_put_arp(&pin.flow_metadata.flow, &headers);
+ pinctrl_handle_put_mac_binding(&pin.flow_metadata.flow, &headers,
+ true);
break;
case ACTION_OPCODE_PUT_DHCP_OPTS:
@@ -413,6 +415,11 @@ process_packet_in(const struct ofp_header *msg)
pinctrl_handle_nd_adv(&headers, &pin.flow_metadata, &userdata);
break;
+ case ACTION_OPCODE_PUT_ND:
+ pinctrl_handle_put_mac_binding(&pin.flow_metadata.flow, &headers,
+ false);
+ break;
+
default:
VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
ntohl(ah->opcode));
@@ -523,7 +530,7 @@ pinctrl_destroy(void)
* and apply them whenever a database transaction is available. */
/* Buffered "put_arp" operation. */
-struct put_arp {
+struct put_arp { /* xxx Rename this to "mac_binding" or something. */
struct hmap_node hmap_node; /* In 'put_arps'. */
long long int timestamp; /* In milliseconds. */
@@ -531,7 +538,7 @@ struct put_arp {
/* Key. */
uint32_t dp_key;
uint32_t port_key;
- ovs_be32 ip;
+ char *ip_s;
/* Value. */
struct eth_addr mac;
@@ -554,14 +561,14 @@ destroy_put_arps(void)
}
static struct put_arp *
-pinctrl_find_put_arp(uint32_t dp_key, uint32_t port_key, ovs_be32 ip,
+pinctrl_find_put_arp(uint32_t dp_key, uint32_t port_key, const char *ip_s,
uint32_t hash)
{
struct put_arp *pa;
HMAP_FOR_EACH_WITH_HASH (pa, hmap_node, hash, &put_arps) {
if (pa->dp_key == dp_key
&& pa->port_key == port_key
- && pa->ip == ip) {
+ && !strcmp(pa->ip_s, ip_s)) {
return pa;
}
}
@@ -569,13 +576,22 @@ pinctrl_find_put_arp(uint32_t dp_key, uint32_t port_key, ovs_be32 ip,
}
static void
-pinctrl_handle_put_arp(const struct flow *md, const struct flow *headers)
+pinctrl_handle_put_mac_binding(const struct flow *md,
+ const struct flow *headers, bool is_ipv4)
{
uint32_t dp_key = ntohll(md->metadata);
uint32_t port_key = md->regs[MFF_LOG_INPORT - MFF_REG0];
- ovs_be32 ip = htonl(md->regs[0]);
- uint32_t hash = hash_3words(dp_key, port_key, (OVS_FORCE uint32_t) ip);
- struct put_arp *pa = pinctrl_find_put_arp(dp_key, port_key, ip, hash);
+ char ip_s[INET6_ADDRSTRLEN];
+
+ /* xxx Kinda hoakey argument. */
+ if (is_ipv4) {
+ inet_ntop(AF_INET, &md->regs[0], ip_s, sizeof(ip_s));
+ } else {
+ ovs_be128 ip6 = hton128(flow_get_xxreg(md, 0));
+ inet_ntop(AF_INET6, &ip6, ip_s, sizeof(ip_s));
+ }
+ uint32_t hash = hash_string(ip_s, (hash_2words(dp_key, port_key)));
+ struct put_arp *pa = pinctrl_find_put_arp(dp_key, port_key, ip_s, hash);
if (!pa) {
if (hmap_count(&put_arps) >= 1000) {
COVERAGE_INC(pinctrl_drop_put_arp);
@@ -586,12 +602,14 @@ pinctrl_handle_put_arp(const struct flow *md, const struct flow *headers)
hmap_insert(&put_arps, &pa->hmap_node, hash);
pa->dp_key = dp_key;
pa->port_key = port_key;
- pa->ip = ip;
+ pa->ip_s = xstrdup(ip_s);
}
pa->timestamp = time_msec();
pa->mac = headers->dl_src;
}
+/* xxx All these functions should change from put_arp to bind_macs or
+ * xxx something. */
static void
run_put_arp(struct controller_ctx *ctx, const struct lport_index *lports,
const struct put_arp *pa)
@@ -611,22 +629,19 @@ run_put_arp(struct controller_ctx *ctx, const struct lport_index *lports,
return;
}
- /* Convert arguments to string form for database. */
- char ip_string[INET_ADDRSTRLEN + 1];
- snprintf(ip_string, sizeof ip_string, IP_FMT, IP_ARGS(pa->ip));
-
+ /* Convert ethernet argument to string form for database. */
char mac_string[ETH_ADDR_STRLEN + 1];
snprintf(mac_string, sizeof mac_string,
ETH_ADDR_FMT, ETH_ADDR_ARGS(pa->mac));
- /* Check for and update an existing IP-MAC binding for this logical
+ /* Check for an update an existing IP-MAC binding for this logical
* port.
*
* XXX This is not very efficient. */
const struct sbrec_mac_binding *b;
SBREC_MAC_BINDING_FOR_EACH (b, ctx->ovnsb_idl) {
if (!strcmp(b->logical_port, pb->logical_port)
- && !strcmp(b->ip, ip_string)) {
+ && !strcmp(b->ip, pa->ip_s)) {
if (strcmp(b->mac, mac_string)) {
sbrec_mac_binding_set_mac(b, mac_string);
}
@@ -637,7 +652,7 @@ run_put_arp(struct controller_ctx *ctx, const struct lport_index *lports,
/* Add new IP-MAC binding for this logical port. */
b = sbrec_mac_binding_insert(ctx->ovnsb_idl_txn);
sbrec_mac_binding_set_logical_port(b, pb->logical_port);
- sbrec_mac_binding_set_ip(b, ip_string);
+ sbrec_mac_binding_set_ip(b, pa->ip_s);
sbrec_mac_binding_set_mac(b, mac_string);
}
@@ -668,6 +683,7 @@ flush_put_arps(void)
{
struct put_arp *pa;
HMAP_FOR_EACH_POP (pa, hmap_node, &put_arps) {
+ free(pa->ip_s);
free(pa);
}
}
@@ -776,6 +776,56 @@ parse_ct_lb_action(struct action_context *ctx)
}
static void
+parse_get_nd_action(struct action_context *ctx)
+{
+ struct mf_subfield port, ip6;
+
+ if (!action_force_match(ctx, LEX_T_LPAREN)
+ || !action_parse_field(ctx, 0, &port)
+ || !action_force_match(ctx, LEX_T_COMMA)
+ || !action_parse_field(ctx, 128, &ip6)
+ || !action_force_match(ctx, LEX_T_RPAREN)) {
+ return;
+ }
+
+ const struct arg args[] = {
+ { &port, MFF_LOG_OUTPORT },
+ { &ip6, MFF_XXREG0 },
+ };
+ setup_args(ctx, args, ARRAY_SIZE(args));
+
+ put_load(0, MFF_ETH_DST, 0, 48, ctx->ofpacts);
+ emit_resubmit(ctx, ctx->ap->arp_ptable);
+
+ restore_args(ctx, args, ARRAY_SIZE(args));
+}
+
+static void
+parse_put_nd_action(struct action_context *ctx)
+{
+ struct mf_subfield port, ip6, mac;
+
+ if (!action_force_match(ctx, LEX_T_LPAREN)
+ || !action_parse_field(ctx, 0, &port)
+ || !action_force_match(ctx, LEX_T_COMMA)
+ || !action_parse_field(ctx, 128, &ip6)
+ || !action_force_match(ctx, LEX_T_COMMA)
+ || !action_parse_field(ctx, 48, &mac)
+ || !action_force_match(ctx, LEX_T_RPAREN)) {
+ return;
+ }
+
+ const struct arg args[] = {
+ { &port, MFF_LOG_INPORT },
+ { &ip6, MFF_XXREG0 },
+ { &mac, MFF_ETH_SRC }
+ };
+ setup_args(ctx, args, ARRAY_SIZE(args));
+ put_controller_op(ctx->ofpacts, ACTION_OPCODE_PUT_ND);
+ restore_args(ctx, args, ARRAY_SIZE(args));
+}
+
+static void
emit_ct(struct action_context *ctx, bool recirc_next, bool commit,
int *ct_mark, int *ct_mark_mask,
ovs_be128 *ct_label, ovs_be128 *ct_label_mask)
@@ -1063,12 +1113,16 @@ parse_action(struct action_context *ctx)
parse_ct_lb_action(ctx);
} else if (lexer_match_id(ctx->lexer, "arp")) {
parse_nested_action(ctx, ACTION_OPCODE_ARP, "ip4");
- } else if (lexer_match_id(ctx->lexer, "nd_adv")) {
- parse_nested_action(ctx, ACTION_OPCODE_ND_ADV, "nd_sol");
} else if (lexer_match_id(ctx->lexer, "get_arp")) {
parse_get_arp_action(ctx);
} else if (lexer_match_id(ctx->lexer, "put_arp")) {
parse_put_arp_action(ctx);
+ } else if (lexer_match_id(ctx->lexer, "nd_adv")) {
+ parse_nested_action(ctx, ACTION_OPCODE_ND_ADV, "nd_sol");
+ } else if (lexer_match_id(ctx->lexer, "get_nd")) {
+ parse_get_nd_action(ctx);
+ } else if (lexer_match_id(ctx->lexer, "put_nd")) {
+ parse_put_nd_action(ctx);
} else {
action_syntax_error(ctx, "expecting action");
}
@@ -78,6 +78,16 @@ enum action_opcode {
* The actions, in OpenFlow 1.3 format, follow the action_header.
*/
ACTION_OPCODE_ND_ADV,
+
+ /* "put_nd(port, ip6, mac)"
+ *
+ * Arguments are passed through the packet metadata and data, as follows:
+ *
+ * MFF_XXREG0 = ip6
+ * MFF_LOG_INPORT = port
+ * MFF_ETH_SRC = mac
+ */
+ ACTION_OPCODE_PUT_ND,
};
/* Header. */
@@ -128,7 +138,8 @@ struct action_params {
uint8_t first_ptable; /* First OpenFlow table. */
uint8_t cur_ltable; /* 0 <= cur_ltable < n_tables. */
uint8_t output_ptable; /* OpenFlow table for 'output' to resubmit. */
- uint8_t arp_ptable; /* OpenFlow table for 'get_arp' to resubmit. */
+ uint8_t arp_ptable; /* OpenFlow table for 'get_arp'/'get_nd' to
+ resubmit. */
};
char *actions_parse(struct lexer *, const struct action_params *,
@@ -2362,6 +2362,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
/* Pass other traffic not already handled to the next table for
* routing. */
ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 0, "1", "next;");
+
+ /* ND advertisement handling. Use advertisements to populate
+ * the logical router's ARP/ND table. */
+ /* xxx We're also supposed to learn on solicitations. */
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "nd_adv",
+ "put_nd(inport, nd.target, nd.tll);");
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 80, "nd_sol",
+ "put_nd(inport, ip6.src, nd.sll);");
}
/* Logical router ingress table 1: IP Input for IPv4. */
@@ -2586,6 +2594,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
ds_clear(&actions);
ds_put_format(&actions,
+ "put_nd(inport, ip6.src, nd.sll); "
"nd_adv { "
"eth.src = %s; "
"ip6.src = %s; "
@@ -2973,8 +2982,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
continue;
}
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 0, "1",
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 0, "ip4",
"get_arp(outport, reg0); next;");
+
+ ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 0, "ip6",
+ "get_nd(outport, xxreg0); next;");
}
/* Local router ingress table 6: ARP request.
@@ -650,6 +650,21 @@ reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain=1.2.3.4); => DHCP option domain
# nd_adv
nd_adv { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; /* Allow sending out inport. */ output; }; => actions=controller(userdata=00.00.00.03.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.00.19.00.10.00.00.00.02.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00), prereqs=nd_sol
+# get_nd
+get_nd(outport, ip6.dst); => actions=push:NXM_NX_XXREG0[],push:NXM_NX_IPV6_DST[],pop:NXM_NX_XXREG0[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_XXREG0[], prereqs=eth.type == 0x86dd
+get_nd(inport, xxreg0); => actions=push:NXM_NX_REG15[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_REG15[], prereqs=1
+get_nd; => Syntax error at `;' expecting `('.
+get_nd(); => Syntax error at `)' expecting field name.
+get_nd(inport); => Syntax error at `)' expecting `,'.
+get_nd(inport ip6.dst); => Syntax error at `ip6.dst' expecting `,'.
+get_nd(inport, ip6.dst; => Syntax error at `;' expecting `)'.
+get_nd(inport, eth.dst); => Cannot use 48-bit field eth.dst[0..47] where 128-bit field is required.
+get_nd(inport, outport); => Cannot use string field outport where numeric field is required.
+get_nd(xxreg0, ip6.dst); => Cannot use numeric field xxreg0 where string field is required.
+
+# put_nd
+put_nd(inport, nd.target, nd.sll); => actions=push:NXM_NX_XXREG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ND_SLL[],push:NXM_NX_ND_TARGET[],pop:NXM_NX_XXREG0[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.04.00.00.00.00),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_XXREG0[], prereqs=((icmp6.type == 0x87 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd)) || (icmp6.type == 0x88 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd))) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd) && ((icmp6.type == 0x87 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd)) || (icmp6.type == 0x88 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd))) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd)
&& ip.t
tl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.type == 0x87 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd)
+