Re: [ovs-dev] [PATCH v5] route-table: Add support for v4 via v6 route.

2024-06-01 Thread William Tu via dev

Hi Ilya,

thanks for your review!
The patch passes my githug-ci
https://github.com/williamtu/ovs/actions/runs/9293675528/job/25577358954

I don't know why it fails/skipped at
https://github.com/ovsrobot/ovs/actions/runs/9294537471

On 5/30/24 11:17 AM, Ilya Maximets wrote:




diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index 508737c53ec6..7266f0990570 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -196,6 +196,69 @@ OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep 
100022eb00012237 | wc -l`
  OVS_VSWITCHD_STOP
  AT_CLEANUP

+AT_SETUP([tunnel_push_pop - v4 via v6 route])
+
+OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy 
ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
+AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], 
[0])
+AT_CHECK([ovs-vsctl add-port int-br t1 -- set Interface t1 type=vxlan \
+   options:remote_ip=1.1.2.92 options:key=123 
ofport_request=1\
+   ], [0])
+
+AT_CHECK([ovs-appctl dpif/show], [0], [dnl
+dummy@ovs-dummy: hit:0 missed:0
+  br0:
+br0 65534/100: (dummy-internal)
+p0 1/1: (dummy)
+  int-br:
+int-br 65534/2: (dummy-internal)
+t1 1/4789: (vxlan: key=123, remote_ip=1.1.2.92)
+])
+
+AT_CHECK([ovs-ofctl add-flow br0 action=normal])
+
+dnl Setup dummy interface IP addresses.
+AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/32], [0], [OK
+])
+AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/64], [0], [OK
+])
+dnl Add a static v4 via v6 route
+AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/32 br0 2001:cafe::10 
src=1.1.2.89], [0], [OK
+])
+
+AT_CHECK([ovs-appctl ovs/route/show | grep br0 | sort], [0], [dnl
+Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local
+Cached: 2001:cafe::/64 dev br0 SRC 2001:cafe::88 local
+User: 1.1.2.92/32 dev br0 GW 2001:cafe::10 SRC 1.1.2.89
+])
+
+dnl Check ARP Snoop
+AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(100),dnl
+eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl
+arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])

This is still not a correct test, we would never receive an ARP
from an IPv6-only network.  This must be an IPv6 NA packet instead.

All in all, I'd expect the following test to work without modifications
(unless I mistyped something):

thanks a lot, this is very clear!
I applied your test and hit issue below

---
AT_SETUP([tunnel_push_pop - v4 via v6 route])

OVS_VSWITCHD_START(
 [add-port br0 p0 \
  -- set Interface p0 type=dummy ofport_request=1 \
  other-config:hwaddr=aa:55:aa:55:00:00])
AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg])
AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy])
AT_CHECK([ovs-vsctl add-port int-br t2 \
   -- set Interface t2 type=geneve \
   options:remote_ip=1.1.2.92 \
   options:key=123 ofport_request=2])

dnl Setup IP addresses.
AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/32], [0], [OK
])
AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/64], [0], [OK
])
dnl Adding a static v4 via v6 route.
AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/32 br0 2001:cafe::10 src=1.1.2.88], 
[0], [OK
])

dnl Checking that a local route for added IP was successfully installed.
AT_CHECK([ovs-appctl ovs/route/show | grep br0 | sort], [0], [dnl
Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local
Cached: 2001:cafe::/64 dev br0 SRC 2001:cafe::88 local
User: 1.1.2.92/32 dev br0 GW 2001:cafe::10 SRC 1.1.2.88
])

AT_CHECK([ovs-ofctl add-flow br0 action=normal])
AT_CHECK([ovs-ofctl add-flow int-br action=normal])

AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap])

dnl Check that v4-over-v6 route is used in the trace and that a tunnel neighbor
dnl lookup miss generates ND and not an ARP.
AT_CHECK([ovs-appctl ofproto/trace int-br in_port=LOCAL \
 | grep -E 'tunnel|neighbor|actions'], [0], [dnl
  -> output to native tunnel
  -> tunneling to 2001:cafe::10 via br0
  -> neighbor cache miss for 2001:cafe::10 on bridge br0, sending ND request
Datapath actions: drop
])

hitting an error here:
so the native tunnel is looking for 1.1.2.92, not the ipv6 address

+++ /root/ovs/tests/testsuite.dir/at-groups/815/stdout    2024-06-01 
17:12:56.83600 +0300

@@ -1,5 +1,5 @@
  -> output to native tunnel
- -> tunneling to 2001:cafe::10 via br0
- -> neighbor cache miss for 2001:cafe::10 on bridge br0, sending ND 
request

+ -> tunneling to 1.1.2.92 via br0
+ -> neighbor cache miss for 1.1.2.92 on bridge br0, sending ARP request
 Datapath actions: drop

I think I still need to change some code in native tunnel...
here although "remote_ip" is 1.1.2.92, but we want to change it to 
2001:cafe::10?




dnl Check that the correct Neighbor Solicitation was sent out via p0.
m4_define([ND_NS_PACKET], [m4_joinall([,],
   

Re: [ovs-dev] [PATCH v5] route-table: Add support for v4 via v6 route.

2024-05-30 Thread Ilya Maximets
On 5/30/24 20:17, Ilya Maximets wrote:
> On 5/30/24 01:27, William Tu wrote:
>> Add route-table support for ipv4 dst via ipv6. One use case is BGP
>> unnumbered, a mechanism that establishes peering sessions without the
>> need to explicitly configure IPv4 addresses on the interfaces involved
>> in the peering. Without using IPv4 address assignments, it uses
>> link-local IPv6 addresses of the directly connected neighbors for
>> peering purposes. For example, BGP might install the following route:
>> $ ip route get 100.87.18.3
>> 100.87.18.3 via inet6 fe80::920a:84ff:fe9e:9570 \
>> dev br-phy src 100.87.18.6
>>
>> Note that the v6 addr fe80::920a:84ff:fe9e:9570 is not being used in
>> the packet header, but only used for lookup the out dev br-phy.
>> Currently OVS can only support either all-ipv4 or all-ipv6, the patch
>> adds support for such use case.
>>
>> Reported-at: 
>> https://mail.openvswitch.org/pipermail/ovs-discuss/2024-January/052908.html
>> Acked-by: Simon Horman 
>> Signed-off-by: William Tu 
>> ---
>> v5: fix minor CI failure
>> v4: feedback from Ilya
>> - add route del test case, wrap around test width
>> - not set neighbor cache manually
>> - on br-phy, use /32 on address in steead of /24
>> compare v3 and v4
>> https://github.com/williamtu/ovs/compare/router..router-v4
>> v3: add vxlan test, remove rfc
>> v2: fix CI error
>> ---
> 
> 
> 
>> diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
>> index 508737c53ec6..7266f0990570 100644
>> --- a/tests/tunnel-push-pop.at
>> +++ b/tests/tunnel-push-pop.at
>> @@ -196,6 +196,69 @@ OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep 
>> 100022eb00012237 | wc -l`
>>  OVS_VSWITCHD_STOP
>>  AT_CLEANUP
>>  
>> +AT_SETUP([tunnel_push_pop - v4 via v6 route])
>> +
>> +OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy 
>> ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
>> +AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br 
>> datapath_type=dummy], [0])
>> +AT_CHECK([ovs-vsctl add-port int-br t1 -- set Interface t1 type=vxlan \
>> +   options:remote_ip=1.1.2.92 options:key=123 
>> ofport_request=1\
>> +   ], [0])
>> +
>> +AT_CHECK([ovs-appctl dpif/show], [0], [dnl
>> +dummy@ovs-dummy: hit:0 missed:0
>> +  br0:
>> +br0 65534/100: (dummy-internal)
>> +p0 1/1: (dummy)
>> +  int-br:
>> +int-br 65534/2: (dummy-internal)
>> +t1 1/4789: (vxlan: key=123, remote_ip=1.1.2.92)
>> +])
>> +
>> +AT_CHECK([ovs-ofctl add-flow br0 action=normal])
>> +
>> +dnl Setup dummy interface IP addresses.
>> +AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/32], [0], [OK
>> +])
>> +AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/64], [0], [OK
>> +])
>> +dnl Add a static v4 via v6 route
>> +AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/32 br0 2001:cafe::10 
>> src=1.1.2.89], [0], [OK
>> +])
>> +
>> +AT_CHECK([ovs-appctl ovs/route/show | grep br0 | sort], [0], [dnl
>> +Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local
>> +Cached: 2001:cafe::/64 dev br0 SRC 2001:cafe::88 local
>> +User: 1.1.2.92/32 dev br0 GW 2001:cafe::10 SRC 1.1.2.89
>> +])
>> +
>> +dnl Check ARP Snoop
>> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(100),dnl
>> +eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl
>> +arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])
> 
> This is still not a correct test, we would never receive an ARP
> from an IPv6-only network.  This must be an IPv6 NA packet instead.
> 
> All in all, I'd expect the following test to work without modifications
> (unless I mistyped something):
> 
> ---
> AT_SETUP([tunnel_push_pop - v4 via v6 route])
> 
> OVS_VSWITCHD_START(
> [add-port br0 p0 \
>  -- set Interface p0 type=dummy ofport_request=1 \
>  other-config:hwaddr=aa:55:aa:55:00:00])
> AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg])
> AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy])
> AT_CHECK([ovs-vsctl add-port int-br t2 \
>   -- set Interface t2 type=geneve \
>   options:remote_ip=1.1.2.92 \
>   options:key=123 ofport_request=2])
> 
> dnl Setup IP addresses.
> AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/32], [0], [OK
> ])
> AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/64], [0], [OK
> ])
> dnl Adding a static v4 via v6 route.
> AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/32 br0 2001:cafe::10 
> src=1.1.2.88], [0], [OK
> ])
> 
> dnl Checking that a local route for added IP was successfully installed.
> AT_CHECK([ovs-appctl ovs/route/show | grep br0 | sort], [0], [dnl
> Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local
> Cached: 2001:cafe::/64 dev br0 SRC 2001:cafe::88 local
> User: 1.1.2.92/32 dev br0 GW 2001:cafe::10 SRC 1.1.2.88
> ])
> 
> AT_CHECK([ovs-ofctl add-flow br0 action=normal])
> AT_CHECK([ovs-ofctl add-flow int-br action=normal])
> 
> 

Re: [ovs-dev] [PATCH v5] route-table: Add support for v4 via v6 route.

2024-05-30 Thread Ilya Maximets
On 5/30/24 01:27, William Tu wrote:
> Add route-table support for ipv4 dst via ipv6. One use case is BGP
> unnumbered, a mechanism that establishes peering sessions without the
> need to explicitly configure IPv4 addresses on the interfaces involved
> in the peering. Without using IPv4 address assignments, it uses
> link-local IPv6 addresses of the directly connected neighbors for
> peering purposes. For example, BGP might install the following route:
> $ ip route get 100.87.18.3
> 100.87.18.3 via inet6 fe80::920a:84ff:fe9e:9570 \
> dev br-phy src 100.87.18.6
> 
> Note that the v6 addr fe80::920a:84ff:fe9e:9570 is not being used in
> the packet header, but only used for lookup the out dev br-phy.
> Currently OVS can only support either all-ipv4 or all-ipv6, the patch
> adds support for such use case.
> 
> Reported-at: 
> https://mail.openvswitch.org/pipermail/ovs-discuss/2024-January/052908.html
> Acked-by: Simon Horman 
> Signed-off-by: William Tu 
> ---
> v5: fix minor CI failure
> v4: feedback from Ilya
> - add route del test case, wrap around test width
> - not set neighbor cache manually
> - on br-phy, use /32 on address in steead of /24
> compare v3 and v4
> https://github.com/williamtu/ovs/compare/router..router-v4
> v3: add vxlan test, remove rfc
> v2: fix CI error
> ---



> diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
> index 508737c53ec6..7266f0990570 100644
> --- a/tests/tunnel-push-pop.at
> +++ b/tests/tunnel-push-pop.at
> @@ -196,6 +196,69 @@ OVS_WAIT_UNTIL([test `ovs-pcap p0.pcap | grep 
> 100022eb00012237 | wc -l`
>  OVS_VSWITCHD_STOP
>  AT_CLEANUP
>  
> +AT_SETUP([tunnel_push_pop - v4 via v6 route])
> +
> +OVS_VSWITCHD_START([add-port br0 p0 -- set Interface p0 type=dummy 
> ofport_request=1 other-config:hwaddr=aa:55:aa:55:00:00])
> +AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy], 
> [0])
> +AT_CHECK([ovs-vsctl add-port int-br t1 -- set Interface t1 type=vxlan \
> +   options:remote_ip=1.1.2.92 options:key=123 
> ofport_request=1\
> +   ], [0])
> +
> +AT_CHECK([ovs-appctl dpif/show], [0], [dnl
> +dummy@ovs-dummy: hit:0 missed:0
> +  br0:
> +br0 65534/100: (dummy-internal)
> +p0 1/1: (dummy)
> +  int-br:
> +int-br 65534/2: (dummy-internal)
> +t1 1/4789: (vxlan: key=123, remote_ip=1.1.2.92)
> +])
> +
> +AT_CHECK([ovs-ofctl add-flow br0 action=normal])
> +
> +dnl Setup dummy interface IP addresses.
> +AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/32], [0], [OK
> +])
> +AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/64], [0], [OK
> +])
> +dnl Add a static v4 via v6 route
> +AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/32 br0 2001:cafe::10 
> src=1.1.2.89], [0], [OK
> +])
> +
> +AT_CHECK([ovs-appctl ovs/route/show | grep br0 | sort], [0], [dnl
> +Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local
> +Cached: 2001:cafe::/64 dev br0 SRC 2001:cafe::88 local
> +User: 1.1.2.92/32 dev br0 GW 2001:cafe::10 SRC 1.1.2.89
> +])
> +
> +dnl Check ARP Snoop
> +AT_CHECK([ovs-appctl netdev-dummy/receive p0 'recirc_id(0),in_port(100),dnl
> +eth(src=f8:bc:12:44:34:b6,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),dnl
> +arp(sip=1.1.2.92,tip=1.1.2.88,op=2,sha=f8:bc:12:44:34:b6,tha=00:00:00:00:00:00)'])

This is still not a correct test, we would never receive an ARP
from an IPv6-only network.  This must be an IPv6 NA packet instead.

All in all, I'd expect the following test to work without modifications
(unless I mistyped something):

---
AT_SETUP([tunnel_push_pop - v4 via v6 route])

OVS_VSWITCHD_START(
[add-port br0 p0 \
 -- set Interface p0 type=dummy ofport_request=1 \
 other-config:hwaddr=aa:55:aa:55:00:00])
AT_CHECK([ovs-appctl vlog/set dpif_netdev:dbg])
AT_CHECK([ovs-vsctl add-br int-br -- set bridge int-br datapath_type=dummy])
AT_CHECK([ovs-vsctl add-port int-br t2 \
  -- set Interface t2 type=geneve \
  options:remote_ip=1.1.2.92 \
  options:key=123 ofport_request=2])

dnl Setup IP addresses.
AT_CHECK([ovs-appctl netdev-dummy/ip4addr br0 1.1.2.88/32], [0], [OK
])
AT_CHECK([ovs-appctl netdev-dummy/ip6addr br0 2001:cafe::88/64], [0], [OK
])
dnl Adding a static v4 via v6 route.
AT_CHECK([ovs-appctl ovs/route/add 1.1.2.92/32 br0 2001:cafe::10 src=1.1.2.88], 
[0], [OK
])

dnl Checking that a local route for added IP was successfully installed.
AT_CHECK([ovs-appctl ovs/route/show | grep br0 | sort], [0], [dnl
Cached: 1.1.2.88/32 dev br0 SRC 1.1.2.88 local
Cached: 2001:cafe::/64 dev br0 SRC 2001:cafe::88 local
User: 1.1.2.92/32 dev br0 GW 2001:cafe::10 SRC 1.1.2.88
])

AT_CHECK([ovs-ofctl add-flow br0 action=normal])
AT_CHECK([ovs-ofctl add-flow int-br action=normal])

AT_CHECK([ovs-vsctl -- set Interface p0 options:tx_pcap=p0.pcap])

dnl Check that v4-over-v6 route is used in the trace and that a tunnel neighbor
dnl lookup miss generates ND and not an ARP.
AT_CHECK([ovs-appctl 

[ovs-dev] [PATCH v5] route-table: Add support for v4 via v6 route.

2024-05-29 Thread William Tu via dev
Add route-table support for ipv4 dst via ipv6. One use case is BGP
unnumbered, a mechanism that establishes peering sessions without the
need to explicitly configure IPv4 addresses on the interfaces involved
in the peering. Without using IPv4 address assignments, it uses
link-local IPv6 addresses of the directly connected neighbors for
peering purposes. For example, BGP might install the following route:
$ ip route get 100.87.18.3
100.87.18.3 via inet6 fe80::920a:84ff:fe9e:9570 \
dev br-phy src 100.87.18.6

Note that the v6 addr fe80::920a:84ff:fe9e:9570 is not being used in
the packet header, but only used for lookup the out dev br-phy.
Currently OVS can only support either all-ipv4 or all-ipv6, the patch
adds support for such use case.

Reported-at: 
https://mail.openvswitch.org/pipermail/ovs-discuss/2024-January/052908.html
Acked-by: Simon Horman 
Signed-off-by: William Tu 
---
v5: fix minor CI failure
v4: feedback from Ilya
- add route del test case, wrap around test width
- not set neighbor cache manually
- on br-phy, use /32 on address in steead of /24
compare v3 and v4
https://github.com/williamtu/ovs/compare/router..router-v4
v3: add vxlan test, remove rfc
v2: fix CI error
---
 lib/ovs-router.c | 39 +++---
 lib/route-table.c| 21 
 ofproto/ofproto-dpif-xlate.c |  3 +-
 tests/ovs-router.at  | 51 +
 tests/system-route.at| 39 ++
 tests/tunnel-push-pop.at | 63 
 6 files changed, 197 insertions(+), 19 deletions(-)

diff --git a/lib/ovs-router.c b/lib/ovs-router.c
index 3d84c9a30a8f..fbf67b666993 100644
--- a/lib/ovs-router.c
+++ b/lib/ovs-router.c
@@ -184,6 +184,11 @@ verify_prefsrc(const struct in6_addr *ip6_dst,
 goto out;
 }
 
+/* Skip the check if not the same address family */
+if (!IN6_IS_ADDR_V4MAPPED(ip6_dst) && IN6_IS_ADDR_V4MAPPED(prefsrc)) {
+goto out;
+}
+
 for (i = 0; i < n_in6; i++) {
 struct in6_addr a1, a2;
 a1 = ipv6_addr_bitand(ip6_dst, [i]);
@@ -415,7 +420,6 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
 unsigned int plen;
 ovs_be32 src = 0;
 ovs_be32 gw = 0;
-bool is_ipv6;
 ovs_be32 ip;
 int err;
 int i;
@@ -423,9 +427,8 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
 if (scan_ipv4_route(argv[1], , )) {
 in6_addr_set_mapped_ipv4(, ip);
 plen += 96;
-is_ipv6 = false;
 } else if (scan_ipv6_route(argv[1], , )) {
-is_ipv6 = true;
+;
 } else {
 unixctl_command_reply_error(conn,
 "Invalid 'ip/plen' parameter");
@@ -438,21 +441,21 @@ ovs_router_add(struct unixctl_conn *conn, int argc,
 continue;
 }
 
-if (is_ipv6) {
-if (ovs_scan(argv[i], "src="IPV6_SCAN_FMT, src6_s) &&
-ipv6_parse(src6_s, )) {
-continue;
-}
-if (ipv6_parse(argv[i], )) {
-continue;
-}
-} else {
-if (ovs_scan(argv[i], "src="IP_SCAN_FMT, IP_SCAN_ARGS())) {
-continue;
-}
-if (ip_parse(argv[i], )) {
-continue;
-}
+if (ovs_scan(argv[i], "src="IPV6_SCAN_FMT, src6_s) &&
+ipv6_parse(src6_s, )) {
+continue;
+}
+
+if (ipv6_parse(argv[i], )) {
+continue;
+}
+
+if (ovs_scan(argv[i], "src="IP_SCAN_FMT, IP_SCAN_ARGS())) {
+continue;
+}
+
+if (ip_parse(argv[i], )) {
+continue;
 }
 
 unixctl_command_reply_error(conn,
diff --git a/lib/route-table.c b/lib/route-table.c
index f1fe32714e8d..58412711888f 100644
--- a/lib/route-table.c
+++ b/lib/route-table.c
@@ -232,6 +232,7 @@ route_table_parse(struct ofpbuf *buf, struct 
route_table_msg *change)
 [RTA_OIF] = { .type = NL_A_U32, .optional = true },
 [RTA_GATEWAY] = { .type = NL_A_U32, .optional = true },
 [RTA_MARK] = { .type = NL_A_U32, .optional = true },
+[RTA_VIA] = { .type = NL_A_UNSPEC, .optional = true },
 [RTA_PREFSRC] = { .type = NL_A_U32, .optional = true },
 [RTA_TABLE] = { .type = NL_A_U32, .optional = true },
 };
@@ -241,6 +242,7 @@ route_table_parse(struct ofpbuf *buf, struct 
route_table_msg *change)
 [RTA_OIF] = { .type = NL_A_U32, .optional = true },
 [RTA_MARK] = { .type = NL_A_U32, .optional = true },
 [RTA_GATEWAY] = { .type = NL_A_IPV6, .optional = true },
+[RTA_VIA] = { .type = NL_A_UNSPEC, .optional = true },
 [RTA_PREFSRC] = { .type = NL_A_IPV6, .optional = true },
 [RTA_TABLE] = { .type = NL_A_U32, .optional = true },
 };
@@ -333,6 +335,25 @@ route_table_parse(struct ofpbuf *buf, struct 
route_table_msg *change)
 nl_attr_get_in6_addr(attrs[RTA_PREFSRC]);