From patchwork Tue Jul 12 06:56:56 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Pettit X-Patchwork-Id: 647322 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from archives.nicira.com (archives.nicira.com [96.126.127.54]) by ozlabs.org (Postfix) with ESMTP id 3rpdKn0Q1Rz9sBX for ; Tue, 12 Jul 2016 20:21:33 +1000 (AEST) Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id AEED4108CC; Tue, 12 Jul 2016 03:20:32 -0700 (PDT) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx1e3.cudamail.com (mx1.cudamail.com [69.90.118.67]) by archives.nicira.com (Postfix) with ESMTPS id 8754610886 for ; Tue, 12 Jul 2016 03:20:27 -0700 (PDT) Received: from bar5.cudamail.com (localhost [127.0.0.1]) by mx1e3.cudamail.com (Postfix) with ESMTPS id 0D6B3420233 for ; Tue, 12 Jul 2016 04:20:27 -0600 (MDT) X-ASG-Debug-ID: 1468318826-09eadd72150c300001-byXFYA Received: from mx3-pf2.cudamail.com ([192.168.14.1]) by bar5.cudamail.com with ESMTP id ZeewyW34y8pKbo6C (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Tue, 12 Jul 2016 04:20:26 -0600 (MDT) X-Barracuda-Envelope-From: jpettit@ovn.org X-Barracuda-RBL-Trusted-Forwarder: 192.168.14.1 Received: from unknown (HELO slow1-d.mail.gandi.net) (217.70.178.86) by mx3-pf2.cudamail.com with SMTP; 12 Jul 2016 10:20:26 -0000 Received-SPF: pass (mx3-pf2.cudamail.com: SPF record at ovn.org designates 217.70.178.86 as permitted sender) X-Barracuda-Apparent-Source-IP: 217.70.178.86 X-Barracuda-RBL-IP: 217.70.178.86 Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [217.70.183.194]) by slow1-d.mail.gandi.net (Postfix) with ESMTP id 128634B667B for ; Tue, 12 Jul 2016 12:17:19 +0200 (CEST) X-Originating-IP: 98.234.50.139 Received: from localhost.localdomain (unknown [98.234.50.139]) (Authenticated sender: jpettit@ovn.org) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id 70E6BC5B44 for ; Tue, 12 Jul 2016 12:17:19 +0200 (CEST) X-CudaMail-Envelope-Sender: jpettit@ovn.org From: Justin Pettit To: dev@openvswitch.org X-CudaMail-Whitelist-To: dev@openvswitch.org X-CudaMail-MID: CM-V2-711003385 X-CudaMail-DTE: 071216 X-CudaMail-Originating-IP: 217.70.178.86 Date: Mon, 11 Jul 2016 23:56:56 -0700 X-ASG-Orig-Subj: [##CM-V2-711003385##][ovn-ipv6 26/26] [RFC] ovn: Add support for IPv6 dynamic bindings. Message-Id: <1468306616-125783-27-git-send-email-jpettit@ovn.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1468306616-125783-1-git-send-email-jpettit@ovn.org> References: <1468306616-125783-1-git-send-email-jpettit@ovn.org> X-Barracuda-Connect: UNKNOWN[192.168.14.1] X-Barracuda-Start-Time: 1468318826 X-Barracuda-Encrypted: DHE-RSA-AES256-SHA X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-ASG-Whitelist: Header =?UTF-8?B?eFwtY3VkYW1haWxcLXdoaXRlbGlzdFwtdG8=?= X-Virus-Scanned: by bsmtpd at cudamail.com X-Barracuda-BRTS-Status: 1 Subject: [ovs-dev] [ovn-ipv6 26/26] [RFC] ovn: Add support for IPv6 dynamic bindings. X-BeenThere: dev@openvswitch.org X-Mailman-Version: 2.1.16 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dev-bounces@openvswitch.org Sender: "dev" TODO: - Need to document new SB actions. - Need to update ovn-northd flows. --- ovn/controller/lflow.c | 25 ++++++++++++++++----- ovn/controller/pinctrl.c | 54 ++++++++++++++++++++++++++++---------------- ovn/lib/actions.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++-- ovn/lib/actions.h | 13 ++++++++++- ovn/northd/ovn-northd.c | 14 +++++++++++- tests/ovn.at | 15 +++++++++++++ 6 files changed, 150 insertions(+), 29 deletions(-) # Contradictionary prerequisites (allowed but not useful): ip4.src = ip6.src[0..31]; => actions=move:NXM_NX_IPV6_SRC[0..31]->NXM_OF_IP_SRC[], prereqs=eth.type == 0x800 && eth.type == 0x86dd ip4.src <-> ip6.src[0..31]; => actions=push:NXM_NX_IPV6_SRC[0..31],push:NXM_OF_IP_SRC[],pop:NXM_NX_IPV6_SRC[0..31],pop:NXM_OF_IP_SRC[], prereqs=eth.type == 0x800 && eth.type == 0x86dd diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c index 10a7e18..5010579 100644 --- a/ovn/controller/lflow.c +++ b/ovn/controller/lflow.c @@ -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); diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c index 77e5dc6..7a92e80 100644 --- a/ovn/controller/pinctrl.c +++ b/ovn/controller/pinctrl.c @@ -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); } } diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c index 57ca580..f65c877 100644 --- a/ovn/lib/actions.c +++ b/ovn/lib/actions.c @@ -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"); } diff --git a/ovn/lib/actions.h b/ovn/lib/actions.h index 2268302..4f0a243 100644 --- a/ovn/lib/actions.h +++ b/ovn/lib/actions.h @@ -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 *, diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c index 991018d..b4f5529 100644 --- a/ovn/northd/ovn-northd.c +++ b/ovn/northd/ovn-northd.c @@ -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. diff --git a/tests/ovn.at b/tests/ovn.at index fb2d3a3..3d7e995 100644 --- a/tests/ovn.at +++ b/tests/ovn.at @@ -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) +