From 6a10d8ed05760ed72607e1b3485808dfb3508f69 Mon Sep 17 00:00:00 2001 From: Nathan Skrzypczak Date: Wed, 18 Feb 2026 17:28:09 +0100 Subject: [PATCH] Enable RA with prefix option handling This patch enables and adds support for handling router advertisements carrying a prefix information option with the flag A unset i.e. the SLAAC disabled, but the L flag set i.e. on-link info. When this is the case, we remove all addresses covered by the prefix received, select the one with the longest prefixlen and reinsert it updating its prefixlen to match the packet info. This is useful when the interface address is set by dhcpv6 and default to /128 when the assigned prefix is actually /64 Signed-off-by: Nathan Skrzypczak --- vpp-manager/vpp_runner.go | 9 ++ vpplink/generated/bindings/rd_cp/rd_cp.ba.go | 123 ++++++++++++++++++ .../generated/bindings/rd_cp/rd_cp_rpc.ba.go | 31 +++++ vpplink/generated/gen.go | 2 +- vpplink/generated/generate.log | 4 +- vpplink/generated/vpp_clone_current.sh | 1 + vpplink/rd_cp.go | 44 +++++++ 7 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 vpplink/generated/bindings/rd_cp/rd_cp.ba.go create mode 100644 vpplink/generated/bindings/rd_cp/rd_cp_rpc.ba.go create mode 100644 vpplink/rd_cp.go diff --git a/vpp-manager/vpp_runner.go b/vpp-manager/vpp_runner.go index 1e64bc846..aa17c0b95 100644 --- a/vpp-manager/vpp_runner.go +++ b/vpp-manager/vpp_runner.go @@ -487,6 +487,15 @@ func (v *VppRunner) configureVppUplinkInterface( return errors.Wrap(err, "Error disabling ipv6 RA on uplink interface") } + // EnableIP6NdAddressAutoconfig adds the uplink interface to the list + // that will be handled by the rd_cp.c vpp process and take care + // of programming VPP with the prefixes received in router advertisments + // with prefix option. This also takes care of SLAAC if configured + err = v.vpp.EnableIP6NdAddressAutoconfig(ifSpec.SwIfIndex) + if err != nil { + return errors.Wrap(err, "Error enabling Ipv6 ND address auto config") + } + err = v.vpp.CnatEnableFeatures(ifSpec.SwIfIndex) if err != nil { return errors.Wrap(err, "Error configuring NAT on uplink interface") diff --git a/vpplink/generated/bindings/rd_cp/rd_cp.ba.go b/vpplink/generated/bindings/rd_cp/rd_cp.ba.go new file mode 100644 index 000000000..ef99758d1 --- /dev/null +++ b/vpplink/generated/bindings/rd_cp/rd_cp.ba.go @@ -0,0 +1,123 @@ +// Code generated by GoVPP's binapi-generator. DO NOT EDIT. + +// Package rd_cp contains generated bindings for API file rd_cp.api. +// +// Contents: +// - 2 messages +package rd_cp + +import ( + interface_types "github.com/projectcalico/vpp-dataplane/v3/vpplink/generated/bindings/interface_types" + api "go.fd.io/govpp/api" + codec "go.fd.io/govpp/codec" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the GoVPP api package it is being compiled against. +// A compilation error at this line likely means your copy of the +// GoVPP api package needs to be updated. +const _ = api.GoVppAPIPackageIsVersion2 + +const ( + APIFile = "rd_cp" + APIVersion = "1.0.1" + VersionCrc = 0x871c3bee +) + +// Enable/disable IPv6 ND address autoconfiguration +// +// and setting up default routes +// - sw_if_index - interface to enable the autoconfigutation on +// - enable - 1 to enable address autoconfiguration, 0 to disable +// - install_default_routes - 1 to enable installing defaut routes, +// 0 to disable installing defaut routes, +// the value is ignored (and taken as 0) +// when enable param is set to 0 +// +// IP6NdAddressAutoconfig defines message 'ip6_nd_address_autoconfig'. +type IP6NdAddressAutoconfig struct { + SwIfIndex interface_types.InterfaceIndex `binapi:"interface_index,name=sw_if_index" json:"sw_if_index,omitempty"` + Enable bool `binapi:"bool,name=enable" json:"enable,omitempty"` + InstallDefaultRoutes bool `binapi:"bool,name=install_default_routes" json:"install_default_routes,omitempty"` +} + +func (m *IP6NdAddressAutoconfig) Reset() { *m = IP6NdAddressAutoconfig{} } +func (*IP6NdAddressAutoconfig) GetMessageName() string { return "ip6_nd_address_autoconfig" } +func (*IP6NdAddressAutoconfig) GetCrcString() string { return "9e14a4a7" } +func (*IP6NdAddressAutoconfig) GetMessageType() api.MessageType { + return api.RequestMessage +} + +func (m *IP6NdAddressAutoconfig) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.SwIfIndex + size += 1 // m.Enable + size += 1 // m.InstallDefaultRoutes + return size +} +func (m *IP6NdAddressAutoconfig) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeUint32(uint32(m.SwIfIndex)) + buf.EncodeBool(m.Enable) + buf.EncodeBool(m.InstallDefaultRoutes) + return buf.Bytes(), nil +} +func (m *IP6NdAddressAutoconfig) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.SwIfIndex = interface_types.InterfaceIndex(buf.DecodeUint32()) + m.Enable = buf.DecodeBool() + m.InstallDefaultRoutes = buf.DecodeBool() + return nil +} + +// IP6NdAddressAutoconfigReply defines message 'ip6_nd_address_autoconfig_reply'. +type IP6NdAddressAutoconfigReply struct { + Retval int32 `binapi:"i32,name=retval" json:"retval,omitempty"` +} + +func (m *IP6NdAddressAutoconfigReply) Reset() { *m = IP6NdAddressAutoconfigReply{} } +func (*IP6NdAddressAutoconfigReply) GetMessageName() string { return "ip6_nd_address_autoconfig_reply" } +func (*IP6NdAddressAutoconfigReply) GetCrcString() string { return "e8d4e804" } +func (*IP6NdAddressAutoconfigReply) GetMessageType() api.MessageType { + return api.ReplyMessage +} + +func (m *IP6NdAddressAutoconfigReply) Size() (size int) { + if m == nil { + return 0 + } + size += 4 // m.Retval + return size +} +func (m *IP6NdAddressAutoconfigReply) Marshal(b []byte) ([]byte, error) { + if b == nil { + b = make([]byte, m.Size()) + } + buf := codec.NewBuffer(b) + buf.EncodeInt32(m.Retval) + return buf.Bytes(), nil +} +func (m *IP6NdAddressAutoconfigReply) Unmarshal(b []byte) error { + buf := codec.NewBuffer(b) + m.Retval = buf.DecodeInt32() + return nil +} + +func init() { file_rd_cp_binapi_init() } +func file_rd_cp_binapi_init() { + api.RegisterMessage((*IP6NdAddressAutoconfig)(nil), "ip6_nd_address_autoconfig_9e14a4a7") + api.RegisterMessage((*IP6NdAddressAutoconfigReply)(nil), "ip6_nd_address_autoconfig_reply_e8d4e804") +} + +// Messages returns list of all messages in this module. +func AllMessages() []api.Message { + return []api.Message{ + (*IP6NdAddressAutoconfig)(nil), + (*IP6NdAddressAutoconfigReply)(nil), + } +} diff --git a/vpplink/generated/bindings/rd_cp/rd_cp_rpc.ba.go b/vpplink/generated/bindings/rd_cp/rd_cp_rpc.ba.go new file mode 100644 index 000000000..2507f127b --- /dev/null +++ b/vpplink/generated/bindings/rd_cp/rd_cp_rpc.ba.go @@ -0,0 +1,31 @@ +// Code generated by GoVPP's binapi-generator. DO NOT EDIT. + +package rd_cp + +import ( + "context" + + api "go.fd.io/govpp/api" +) + +// RPCService defines RPC service rd_cp. +type RPCService interface { + IP6NdAddressAutoconfig(ctx context.Context, in *IP6NdAddressAutoconfig) (*IP6NdAddressAutoconfigReply, error) +} + +type serviceClient struct { + conn api.Connection +} + +func NewServiceClient(conn api.Connection) RPCService { + return &serviceClient{conn} +} + +func (c *serviceClient) IP6NdAddressAutoconfig(ctx context.Context, in *IP6NdAddressAutoconfig) (*IP6NdAddressAutoconfigReply, error) { + out := new(IP6NdAddressAutoconfigReply) + err := c.conn.Invoke(ctx, in, out) + if err != nil { + return nil, err + } + return out, api.RetvalToVPPApiError(out.Retval) +} diff --git a/vpplink/generated/gen.go b/vpplink/generated/gen.go index 20cd02371..f8aab2c16 100644 --- a/vpplink/generated/gen.go +++ b/vpplink/generated/gen.go @@ -8,4 +8,4 @@ import ( ) //go:generate go build -buildmode=plugin -o ./.bin/vpplink_plugin.so github.com/calico-vpp/vpplink/pkg -//go:generate go run go.fd.io/govpp/cmd/binapi-generator --no-version-info --no-source-path-info --gen rpc,./.bin/vpplink_plugin.so -o ./bindings --input $VPP_DIR ikev2 gso arp interface ip ipip ipsec ip_neighbor tapv2 nat44_ed cnat af_packet feature ip6_nd punt vxlan af_xdp vlib virtio avf wireguard capo memif acl abf crypto_sw_scheduler sr rdma vmxnet3 pbl memclnt session vpe urpf classify ip_session_redirect +//go:generate go run go.fd.io/govpp/cmd/binapi-generator --no-version-info --no-source-path-info --gen rpc,./.bin/vpplink_plugin.so -o ./bindings --input $VPP_DIR ikev2 gso arp interface ip ipip ipsec ip_neighbor tapv2 nat44_ed cnat af_packet feature ip6_nd punt vxlan af_xdp vlib virtio avf wireguard capo memif acl abf crypto_sw_scheduler sr rdma vmxnet3 pbl memclnt session vpe urpf classify ip_session_redirect rd_cp diff --git a/vpplink/generated/generate.log b/vpplink/generated/generate.log index 1c63d71e1..d0b17510f 100755 --- a/vpplink/generated/generate.log +++ b/vpplink/generated/generate.log @@ -1,4 +1,4 @@ -VPP Version : 25.06.0-28~g4787d90bc +VPP Version : 25.06.0-30~g3c6e6ed86 Binapi-generator version : v0.11.0 VPP Base commit : 47505bc21 misc: Initial changes for stable/2506 branch ------------------ Cherry picked commits -------------------- @@ -6,7 +6,9 @@ capo: Calico Policies plugin acl: acl-plugin custom policies cnat: [WIP] no k8s maglev from pods pbl: Port based balancer +gerrit:44961/2 ip6-nd: support RA pfx info option with flag L&!A gerrit:44903/1 vxlan: reset next_dpo on delete +gerrit:44900/1 tap: backport fix promiscuous mode gerrit:44855/6 tap: backport VHOST_SET_FORK_FROM_OWNER support gerrit:44350/2 vnet: fix unicast NA handling in ND proxy gerrit:43723/3 session svm: fix session migrate attach data corruption diff --git a/vpplink/generated/vpp_clone_current.sh b/vpplink/generated/vpp_clone_current.sh index 04d957189..e5644a936 100755 --- a/vpplink/generated/vpp_clone_current.sh +++ b/vpplink/generated/vpp_clone_current.sh @@ -150,6 +150,7 @@ git_cherry_pick refs/changes/55/44855/6 # 44855: tap: backport VHOST_SET_FORK_FR git_cherry_pick refs/changes/00/44900/1 # 44900: tap: backport fix promiscuous mode | https://gerrit.fd.io/r/c/vpp/+/44900 git_cherry_pick refs/changes/03/44903/1 # 44903: vxlan: reset next_dpo on delete | https://gerrit.fd.io/r/c/vpp/+/44903 +git_cherry_pick refs/changes/61/44961/2 # 44961: ip6-nd: support RA pfx info option with flag L&!A | https://gerrit.fd.io/r/c/vpp/+/44961 # --------------- private plugins --------------- # Generated with 'git format-patch --zero-commit -o ./patches/ HEAD^^^' diff --git a/vpplink/rd_cp.go b/vpplink/rd_cp.go new file mode 100644 index 000000000..1cd348001 --- /dev/null +++ b/vpplink/rd_cp.go @@ -0,0 +1,44 @@ +// Copyright (C) 2021 Cisco Systems Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vpplink + +import ( + "fmt" + + "github.com/projectcalico/vpp-dataplane/v3/vpplink/generated/bindings/interface_types" + "github.com/projectcalico/vpp-dataplane/v3/vpplink/generated/bindings/rd_cp" +) + +func (v *VppLink) enableDisableIP6NdAddressAutoconfig(swIfIndex uint32, enable bool) (err error) { + client := rd_cp.NewServiceClient(v.GetConnection()) + + _, err = client.IP6NdAddressAutoconfig(v.GetContext(), &rd_cp.IP6NdAddressAutoconfig{ + SwIfIndex: interface_types.InterfaceIndex(swIfIndex), + Enable: enable, + }) + if err != nil { + return fmt.Errorf("failed to call IP6NdAddressAutoconfig: %w", err) + } + return nil +} + +func (v *VppLink) EnableIP6NdAddressAutoconfig(swIfIndex uint32) (err error) { + return v.enableDisableIP6NdAddressAutoconfig(swIfIndex, true) +} + +func (v *VppLink) DisableIP6NdAddressAutoconfig(swIfIndex uint32) (err error) { + return v.enableDisableIP6NdAddressAutoconfig(swIfIndex, false) +}