2019-08-27 07:52:58 +00:00
|
|
|
// Copyright (c) 2019 Multus Authors
|
|
|
|
//
|
|
|
|
// 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 netutils
|
|
|
|
|
|
|
|
import (
|
2021-12-03 15:48:50 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/containernetworking/cni/libcni"
|
2019-08-27 07:52:58 +00:00
|
|
|
"github.com/containernetworking/plugins/pkg/ns"
|
|
|
|
"github.com/vishvananda/netlink"
|
2021-03-16 07:08:53 +00:00
|
|
|
"gopkg.in/k8snetworkplumbingwg/multus-cni.v3/pkg/logging"
|
2019-08-27 07:52:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// DeleteDefaultGW removes the default gateway from marked interfaces.
|
thick-plugin: refactor multus
Multus is refactored as a thick plugin, featuring 2 main components:
- a server listening to a unix domain socket, running in a pod
- a shim, a binary on the host that will send JSON requests built from
its environment / stdin values to the aforementioned server.
The pod where the multus daemon is running must share the host's PID
namespace.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
react to maintainers review
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick, deployment: update the daemonset spec
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick, config: validate the cni config passed by the runtime
Without this patch, we're blindly trusting anything sent by the server.
This way, we assure the requests arriving at the multus controller are
valid before hand.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick: model client / server config
Also add a new command line parameter on the multus controller, pointing
it to the server configuration.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
SQUASH candidate, thick, config: cleanup the configuration
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
multus: use args.args instead of an env variable
CNI is already filling the args structure; we should consume that
rather than rely on the environment variables.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
unit tests: remove weird tests that check an impossible scenario
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
docs, thick: document the thick plugin variant
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick, server, multus: re-use common types
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-12-16 17:18:35 +00:00
|
|
|
func DeleteDefaultGW(netnsPath string, ifName string) error {
|
|
|
|
netns, err := ns.GetNS(netnsPath)
|
2019-08-27 07:52:58 +00:00
|
|
|
if err != nil {
|
2021-12-03 15:48:50 +00:00
|
|
|
return logging.Errorf("DeleteDefaultGW: Error getting namespace %v", err)
|
2019-08-27 07:52:58 +00:00
|
|
|
}
|
|
|
|
defer netns.Close()
|
|
|
|
|
|
|
|
err = netns.Do(func(_ ns.NetNS) error {
|
|
|
|
var err error
|
|
|
|
link, _ := netlink.LinkByName(ifName)
|
|
|
|
routes, _ := netlink.RouteList(link, netlink.FAMILY_ALL)
|
|
|
|
for _, nlroute := range routes {
|
|
|
|
if nlroute.Dst == nil {
|
|
|
|
err = netlink.RouteDel(&nlroute)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
})
|
2021-12-03 15:48:50 +00:00
|
|
|
return err
|
2019-08-27 07:52:58 +00:00
|
|
|
}
|
2019-10-23 15:40:44 +00:00
|
|
|
|
|
|
|
// SetDefaultGW adds a default gateway on a specific interface
|
thick-plugin: refactor multus
Multus is refactored as a thick plugin, featuring 2 main components:
- a server listening to a unix domain socket, running in a pod
- a shim, a binary on the host that will send JSON requests built from
its environment / stdin values to the aforementioned server.
The pod where the multus daemon is running must share the host's PID
namespace.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
react to maintainers review
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick, deployment: update the daemonset spec
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick, config: validate the cni config passed by the runtime
Without this patch, we're blindly trusting anything sent by the server.
This way, we assure the requests arriving at the multus controller are
valid before hand.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick: model client / server config
Also add a new command line parameter on the multus controller, pointing
it to the server configuration.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
SQUASH candidate, thick, config: cleanup the configuration
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
multus: use args.args instead of an env variable
CNI is already filling the args structure; we should consume that
rather than rely on the environment variables.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
unit tests: remove weird tests that check an impossible scenario
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
docs, thick: document the thick plugin variant
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick, server, multus: re-use common types
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-12-16 17:18:35 +00:00
|
|
|
func SetDefaultGW(netnsPath string, ifName string, gateways []net.IP) error {
|
2019-10-23 15:40:44 +00:00
|
|
|
// This ensures we're acting within the net namespace for the pod.
|
thick-plugin: refactor multus
Multus is refactored as a thick plugin, featuring 2 main components:
- a server listening to a unix domain socket, running in a pod
- a shim, a binary on the host that will send JSON requests built from
its environment / stdin values to the aforementioned server.
The pod where the multus daemon is running must share the host's PID
namespace.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
react to maintainers review
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick, deployment: update the daemonset spec
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick, config: validate the cni config passed by the runtime
Without this patch, we're blindly trusting anything sent by the server.
This way, we assure the requests arriving at the multus controller are
valid before hand.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick: model client / server config
Also add a new command line parameter on the multus controller, pointing
it to the server configuration.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
SQUASH candidate, thick, config: cleanup the configuration
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
multus: use args.args instead of an env variable
CNI is already filling the args structure; we should consume that
rather than rely on the environment variables.
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
unit tests: remove weird tests that check an impossible scenario
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
docs, thick: document the thick plugin variant
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
thick, server, multus: re-use common types
Signed-off-by: Miguel Duarte Barroso <mdbarroso@redhat.com>
2021-12-16 17:18:35 +00:00
|
|
|
netns, err := ns.GetNS(netnsPath)
|
2019-10-23 15:40:44 +00:00
|
|
|
if err != nil {
|
2021-12-03 15:48:50 +00:00
|
|
|
return logging.Errorf("SetDefaultGW: Error getting namespace %v", err)
|
2019-10-23 15:40:44 +00:00
|
|
|
}
|
|
|
|
defer netns.Close()
|
|
|
|
|
|
|
|
// Do this within the net namespace.
|
|
|
|
err = netns.Do(func(_ ns.NetNS) error {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
// Pick up the link info as we need the index.
|
2021-12-03 15:48:50 +00:00
|
|
|
link, err := netlink.LinkByName(ifName)
|
|
|
|
if err != nil {
|
|
|
|
return logging.Errorf("SetDefaultGW: Error getting link %v", err)
|
|
|
|
}
|
2019-10-23 15:40:44 +00:00
|
|
|
|
|
|
|
// Cycle through all the desired gateways.
|
|
|
|
for _, gw := range gateways {
|
|
|
|
|
|
|
|
// Create a new route (note: dst is nil by default)
|
|
|
|
logging.Debugf("SetDefaultGW: Adding default route on %v (index: %v) to %v", ifName, link.Attrs().Index, gw)
|
|
|
|
newDefaultRoute := netlink.Route{
|
|
|
|
LinkIndex: link.Attrs().Index,
|
|
|
|
Gw: gw,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform the creation of the default route....
|
|
|
|
err = netlink.RouteAdd(&newDefaultRoute)
|
|
|
|
if err != nil {
|
|
|
|
logging.Errorf("SetDefaultGW: Error adding route: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
|
2021-12-03 15:48:50 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteDefaultGWCache updates libcni cache to remove default gateway routes in result
|
|
|
|
func DeleteDefaultGWCache(cacheDir string, rt *libcni.RuntimeConf, netName string, ifName string, ipv4, ipv6 bool) error {
|
|
|
|
cacheFile := filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName))
|
|
|
|
|
|
|
|
cache, err := ioutil.ReadFile(cacheFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
logging.Debugf("DeleteDefaultGWCache: update cache to delete GW from: %s", string(cache))
|
|
|
|
newCache, err := deleteDefaultGWCacheBytes(cache, ipv4, ipv6)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
logging.Debugf("DeleteDefaultGWCache: update cache to delete GW: %s", string(newCache))
|
|
|
|
return ioutil.WriteFile(cacheFile, newCache, 0600)
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteDefaultGWCacheBytes(cacheFile []byte, ipv4, ipv6 bool) ([]byte, error) {
|
|
|
|
var cachedInfo map[string]interface{}
|
|
|
|
if err := json.Unmarshal(cacheFile, &cachedInfo); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-10-23 15:40:44 +00:00
|
|
|
|
2021-12-03 15:48:50 +00:00
|
|
|
// try to get result
|
|
|
|
_, ok := cachedInfo["result"]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("cannot get result from cache")
|
|
|
|
}
|
|
|
|
|
|
|
|
resultJSON, ok := cachedInfo["result"].(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong result type: %v", cachedInfo["result"])
|
|
|
|
}
|
|
|
|
newResult, err := deleteDefaultGWResult(resultJSON, ipv4, ipv6)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cachedInfo["result"] = newResult
|
|
|
|
|
|
|
|
newCache, err := json.Marshal(cachedInfo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to encode json: %v", err)
|
|
|
|
}
|
|
|
|
return newCache, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteDefaultGWResultRoutes(routes []interface{}, dstGW string) ([]interface{}, error) {
|
|
|
|
for i, r := range routes {
|
|
|
|
route, ok := r.(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong route format: %v", r)
|
|
|
|
}
|
|
|
|
_, ok = route["dst"]
|
|
|
|
if ok {
|
|
|
|
dst, ok := route["dst"].(string)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong dst format: %v", route["dst"])
|
|
|
|
}
|
|
|
|
if dst == dstGW {
|
|
|
|
routes = append(routes[:i], routes[i+1:]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return routes, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteDefaultGWResult(result map[string]interface{}, ipv4, ipv6 bool) (map[string]interface{}, error) {
|
|
|
|
// try to get cniVersion from result
|
|
|
|
_, ok := result["cniVersion"]
|
|
|
|
if !ok {
|
|
|
|
// fallback to processing result for old cni version(0.1.0/0.2.0)
|
|
|
|
return deleteDefaultGWResult020(result, ipv4, ipv6)
|
|
|
|
}
|
|
|
|
|
|
|
|
cniVersion, ok := result["cniVersion"].(string)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong cniVersion format: %v", result["cniVersion"])
|
|
|
|
}
|
|
|
|
|
|
|
|
if cniVersion == "0.1.0" || cniVersion == "0.2.0" {
|
|
|
|
// fallback to processing result for old cni version(0.1.0/0.2.0)
|
|
|
|
return deleteDefaultGWResult020(result, ipv4, ipv6)
|
|
|
|
}
|
|
|
|
|
|
|
|
if cniVersion != "0.3.0" && cniVersion != "0.3.1" && cniVersion != "0.4.0" && cniVersion != "1.0.0" {
|
|
|
|
return nil, fmt.Errorf("not supported version: %s", cniVersion)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, ok = result["routes"]
|
2022-01-04 16:33:58 +00:00
|
|
|
if !ok {
|
2021-12-03 15:48:50 +00:00
|
|
|
// No route in result, hence we do nothing
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
routes, ok := result["routes"].([]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong routes format: %v", result["routes"])
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
// delete IPv4 default routes
|
|
|
|
if ipv4 {
|
|
|
|
routes, err = deleteDefaultGWResultRoutes(routes, "0.0.0.0/0")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ipv6 {
|
|
|
|
routes, err = deleteDefaultGWResultRoutes(routes, "::0/0")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result["routes"] = routes
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func deleteDefaultGWResult020(result map[string]interface{}, ipv4, ipv6 bool) (map[string]interface{}, error) {
|
|
|
|
var err error
|
|
|
|
if ipv4 {
|
|
|
|
_, ok := result["ip4"]
|
|
|
|
if ok {
|
|
|
|
ip4, ok := result["ip4"].(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong ip4 format: %v", result["ip4"])
|
|
|
|
}
|
|
|
|
|
|
|
|
_, ok = ip4["routes"]
|
|
|
|
if ok {
|
|
|
|
routes, ok := ip4["routes"].([]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong ip4 routes format: %v", ip4["routes"])
|
|
|
|
}
|
|
|
|
|
|
|
|
routes, err = deleteDefaultGWResultRoutes(routes, "0.0.0.0/0")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ip4["routes"] = routes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ipv6 {
|
|
|
|
_, ok := result["ip6"]
|
|
|
|
if ok {
|
|
|
|
ip6, ok := result["ip6"].(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong ip6 format: %v", result["ip6"])
|
|
|
|
}
|
|
|
|
|
|
|
|
_, ok = ip6["routes"]
|
|
|
|
if ok {
|
|
|
|
routes, ok := ip6["routes"].([]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong ip6 routes format: %v", ip6["routes"])
|
|
|
|
}
|
|
|
|
|
|
|
|
routes, err = deleteDefaultGWResultRoutes(routes, "::0/0")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ip6["routes"] = routes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddDefaultGWCache updates libcni cache to add default gateway result
|
|
|
|
func AddDefaultGWCache(cacheDir string, rt *libcni.RuntimeConf, netName string, ifName string, gw []net.IP) error {
|
|
|
|
cacheFile := filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName))
|
|
|
|
|
|
|
|
cache, err := ioutil.ReadFile(cacheFile)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
logging.Debugf("AddDefaultGWCache: update cache to add GW from: %s", string(cache))
|
|
|
|
newCache, err := addDefaultGWCacheBytes(cache, gw)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
logging.Debugf("AddDefaultGWCache: update cache to add GW: %s", string(newCache))
|
|
|
|
return ioutil.WriteFile(cacheFile, newCache, 0600)
|
|
|
|
}
|
|
|
|
|
|
|
|
func addDefaultGWCacheBytes(cacheFile []byte, gw []net.IP) ([]byte, error) {
|
|
|
|
var cachedInfo map[string]interface{}
|
|
|
|
if err := json.Unmarshal(cacheFile, &cachedInfo); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// try to get result
|
|
|
|
_, ok := cachedInfo["result"]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("cannot get result from cache")
|
|
|
|
}
|
|
|
|
|
|
|
|
resultJSON, ok := cachedInfo["result"].(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong result type: %v", cachedInfo["result"])
|
|
|
|
}
|
|
|
|
newResult, err := addDefaultGWResult(resultJSON, gw)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cachedInfo["result"] = newResult
|
|
|
|
|
|
|
|
newCache, err := json.Marshal(cachedInfo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to encode json: %v", err)
|
|
|
|
}
|
|
|
|
return newCache, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func addDefaultGWResult(result map[string]interface{}, gw []net.IP) (map[string]interface{}, error) {
|
|
|
|
// try to get cniVersion from result
|
|
|
|
_, ok := result["cniVersion"]
|
|
|
|
if !ok {
|
|
|
|
// fallback to processing result for old cni version(0.1.0/0.2.0)
|
|
|
|
return addDefaultGWResult020(result, gw)
|
|
|
|
}
|
|
|
|
|
|
|
|
cniVersion, ok := result["cniVersion"].(string)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong cniVersion format: %v", result["cniVersion"])
|
|
|
|
}
|
|
|
|
|
|
|
|
if cniVersion == "0.1.0" || cniVersion == "0.2.0" {
|
|
|
|
// fallback to processing result for old cni version(0.1.0/0.2.0)
|
|
|
|
return addDefaultGWResult020(result, gw)
|
|
|
|
}
|
|
|
|
|
|
|
|
if cniVersion != "0.3.0" && cniVersion != "0.3.1" && cniVersion != "0.4.0" && cniVersion != "1.0.0" {
|
|
|
|
return nil, fmt.Errorf("not supported version: %s", cniVersion)
|
|
|
|
}
|
|
|
|
|
|
|
|
routes := []interface{}{}
|
|
|
|
_, ok = result["routes"]
|
2022-01-04 16:33:58 +00:00
|
|
|
if ok {
|
2021-12-03 15:48:50 +00:00
|
|
|
routes, ok = result["routes"].([]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong routes format: %v", result["routes"])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, g := range gw {
|
|
|
|
dst := "0.0.0.0/0"
|
|
|
|
if g.To4() == nil {
|
|
|
|
dst = "::0/0"
|
|
|
|
}
|
|
|
|
routes = append(routes, map[string]string{
|
|
|
|
"dst": dst,
|
2022-01-04 16:33:58 +00:00
|
|
|
"gw": g.String(),
|
2021-12-03 15:48:50 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
result["routes"] = routes
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func addDefaultGWResult020(result map[string]interface{}, gw []net.IP) (map[string]interface{}, error) {
|
|
|
|
for _, g := range gw {
|
|
|
|
if g.To4() != nil {
|
|
|
|
_, ok := result["ip4"]
|
|
|
|
if ok {
|
|
|
|
ip4, ok := result["ip4"].(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong ip4 format: %v", result["ip4"])
|
|
|
|
}
|
|
|
|
routes := []interface{}{}
|
|
|
|
_, ok = ip4["routes"]
|
|
|
|
if ok {
|
|
|
|
routes, ok = ip4["routes"].([]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong ip4 routes format: %v", ip4["routes"])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ip4["routes"] = append(routes, map[string]string{
|
|
|
|
"dst": "0.0.0.0/0",
|
2022-01-04 16:33:58 +00:00
|
|
|
"gw": g.String(),
|
2021-12-03 15:48:50 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_, ok := result["ip6"]
|
|
|
|
if ok {
|
|
|
|
ip6, ok := result["ip6"].(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong ip6 format: %v", result["ip4"])
|
|
|
|
}
|
|
|
|
routes := []interface{}{}
|
|
|
|
_, ok = ip6["routes"]
|
|
|
|
if ok {
|
|
|
|
routes, ok = ip6["routes"].([]interface{})
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("wrong ip6 routes format: %v", ip6["routes"])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ip6["routes"] = append(routes, map[string]string{
|
|
|
|
"dst": "::/0",
|
2022-01-04 16:33:58 +00:00
|
|
|
"gw": g.String(),
|
2021-12-03 15:48:50 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result, nil
|
2019-10-23 15:40:44 +00:00
|
|
|
}
|