2019-01-01 08:23:01 +00:00
//
2019-01-09 16:54:15 +00:00
// Copyright 2015 flannel authors
2019-01-01 08:23:01 +00:00
// 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 flannel
import (
"fmt"
"net"
"os"
"path/filepath"
"sync"
2023-02-06 15:17:37 +00:00
"github.com/flannel-io/flannel/pkg/backend"
2021-03-18 22:40:29 +00:00
"github.com/flannel-io/flannel/pkg/ip"
2023-02-06 15:17:37 +00:00
"github.com/flannel-io/flannel/pkg/subnet/kube"
2024-05-23 15:48:12 +00:00
"github.com/flannel-io/flannel/pkg/trafficmngr/iptables"
2024-05-24 07:52:16 +00:00
"github.com/joho/godotenv"
2023-08-31 15:39:33 +00:00
"github.com/pkg/errors"
2021-12-21 13:32:34 +00:00
"github.com/sirupsen/logrus"
2019-01-01 08:23:01 +00:00
"golang.org/x/net/context"
// Backends need to be imported for their init() to get executed and them to register
2023-02-06 15:17:37 +00:00
_ "github.com/flannel-io/flannel/pkg/backend/extension"
_ "github.com/flannel-io/flannel/pkg/backend/hostgw"
_ "github.com/flannel-io/flannel/pkg/backend/ipsec"
_ "github.com/flannel-io/flannel/pkg/backend/vxlan"
_ "github.com/flannel-io/flannel/pkg/backend/wireguard"
2019-01-01 08:23:01 +00:00
)
const (
subnetFile = "/run/flannel/subnet.env"
)
2022-09-26 12:56:55 +00:00
var (
FlannelBaseAnnotation = "flannel.alpha.coreos.com"
FlannelExternalIPv4Annotation = FlannelBaseAnnotation + "/public-ip-overwrite"
FlannelExternalIPv6Annotation = FlannelBaseAnnotation + "/public-ipv6-overwrite"
)
2023-12-19 22:25:38 +00:00
func flannel ( ctx context . Context , flannelIface * net . Interface , flannelConf , kubeConfigFile string , flannelIPv6Masq bool , netMode int ) error {
2021-08-17 09:27:54 +00:00
extIface , err := LookupExtInterface ( flannelIface , netMode )
2019-01-01 08:23:01 +00:00
if err != nil {
2023-08-31 15:39:33 +00:00
return errors . Wrap ( err , "failed to find the interface" )
2019-01-01 08:23:01 +00:00
}
2023-02-06 15:17:37 +00:00
sm , err := kube . NewSubnetManager ( ctx ,
"" ,
kubeConfigFile ,
FlannelBaseAnnotation ,
flannelConf ,
2023-12-19 03:14:02 +00:00
false )
2019-01-01 08:23:01 +00:00
if err != nil {
2023-08-31 15:39:33 +00:00
return errors . Wrap ( err , "failed to create the SubnetManager" )
2019-01-01 08:23:01 +00:00
}
config , err := sm . GetNetworkConfig ( ctx )
if err != nil {
2023-08-31 15:39:33 +00:00
return errors . Wrap ( err , "failed to get the network config" )
2019-01-01 08:23:01 +00:00
}
// Create a backend manager then use it to create the backend and register the network with it.
bm := backend . NewManager ( ctx , sm , extIface )
be , err := bm . GetBackend ( config . BackendType )
if err != nil {
2023-08-31 15:39:33 +00:00
return errors . Wrap ( err , "failed to create the flannel backend" )
2019-01-01 08:23:01 +00:00
}
2021-03-18 22:40:29 +00:00
bn , err := be . RegisterNetwork ( ctx , & sync . WaitGroup { } , config )
2019-01-01 08:23:01 +00:00
if err != nil {
2023-08-31 15:39:33 +00:00
return errors . Wrap ( err , "failed to register flannel network" )
2019-01-01 08:23:01 +00:00
}
2024-05-24 07:52:16 +00:00
trafficMngr := & iptables . IPTablesManager { }
err = trafficMngr . Init ( ctx , & sync . WaitGroup { } )
if err != nil {
return errors . Wrap ( err , "failed to initialize flannel ipTables manager" )
}
2019-01-01 08:23:01 +00:00
2022-03-04 17:06:29 +00:00
if netMode == ( ipv4 + ipv6 ) || netMode == ipv4 {
2024-05-24 07:52:16 +00:00
if config . Network . Empty ( ) {
return errors . New ( "ipv4 mode requested but no ipv4 network provided" )
2023-02-06 15:17:37 +00:00
}
2022-03-04 17:06:29 +00:00
}
2019-01-01 08:23:01 +00:00
2024-05-24 07:52:16 +00:00
//setup masq rules
prevNetwork := ReadCIDRFromSubnetFile ( subnetFile , "FLANNEL_NETWORK" )
prevSubnet := ReadCIDRFromSubnetFile ( subnetFile , "FLANNEL_SUBNET" )
prevIPv6Network := ReadIP6CIDRFromSubnetFile ( subnetFile , "FLANNEL_IPV6_NETWORK" )
prevIPv6Subnet := ReadIP6CIDRFromSubnetFile ( subnetFile , "FLANNEL_IPV6_SUBNET" )
if flannelIPv6Masq {
err = trafficMngr . SetupAndEnsureMasqRules ( ctx , config . Network , prevSubnet , prevNetwork , config . IPv6Network , prevIPv6Subnet , prevIPv6Network , bn . Lease ( ) , 60 )
} else {
//set empty flannel ipv6 Network to prevent masquerading
err = trafficMngr . SetupAndEnsureMasqRules ( ctx , config . Network , prevSubnet , prevNetwork , ip . IP6Net { } , prevIPv6Subnet , prevIPv6Network , bn . Lease ( ) , 60 )
}
if err != nil {
return errors . Wrap ( err , "failed to setup masq rules" )
2022-01-12 16:09:38 +00:00
}
2024-05-24 07:52:16 +00:00
//setup forward rules
trafficMngr . SetupAndEnsureForwardRules ( ctx , config . Network , config . IPv6Network , 50 )
2022-03-04 17:06:29 +00:00
if err := WriteSubnetFile ( subnetFile , config . Network , config . IPv6Network , true , bn , netMode ) ; err != nil {
2019-01-01 08:23:01 +00:00
// Continue, even though it failed.
2021-12-21 13:32:34 +00:00
logrus . Warningf ( "Failed to write flannel subnet file: %s" , err )
2019-01-01 08:23:01 +00:00
} else {
2021-12-21 13:32:34 +00:00
logrus . Infof ( "Wrote flannel subnet file to %s" , subnetFile )
2019-01-01 08:23:01 +00:00
}
// Start "Running" the backend network. This will block until the context is done so run in another goroutine.
2021-12-21 13:32:34 +00:00
logrus . Info ( "Running flannel backend." )
2019-01-01 08:23:01 +00:00
bn . Run ( ctx )
return nil
}
2021-08-17 09:27:54 +00:00
func LookupExtInterface ( iface * net . Interface , netMode int ) ( * backend . ExternalInterface , error ) {
2022-03-02 14:01:25 +00:00
var ifaceAddr [ ] net . IP
var ifacev6Addr [ ] net . IP
2019-01-01 08:23:01 +00:00
var err error
2019-03-19 23:28:43 +00:00
if iface == nil {
2021-12-21 13:32:34 +00:00
logrus . Debug ( "No interface defined for flannel in the config. Fetching the default gateway interface" )
2022-03-04 17:06:29 +00:00
if netMode == ipv4 || netMode == ( ipv4 + ipv6 ) {
if iface , err = ip . GetDefaultGatewayInterface ( ) ; err != nil {
2023-08-31 15:39:33 +00:00
return nil , errors . Wrap ( err , "failed to get default interface" )
2022-03-04 17:06:29 +00:00
}
} else {
if iface , err = ip . GetDefaultV6GatewayInterface ( ) ; err != nil {
2023-08-31 15:39:33 +00:00
return nil , errors . Wrap ( err , "failed to get default interface" )
2022-03-04 17:06:29 +00:00
}
2019-03-19 23:28:43 +00:00
}
2019-01-01 08:23:01 +00:00
}
2021-12-21 13:32:34 +00:00
logrus . Debugf ( "The interface %s will be used by flannel" , iface . Name )
2019-01-01 08:23:01 +00:00
2022-03-08 08:34:25 +00:00
switch netMode {
case ipv4 :
2022-03-07 10:05:08 +00:00
ifaceAddr , err = ip . GetInterfaceIP4Addrs ( iface )
if err != nil {
2023-08-31 15:39:33 +00:00
return nil , errors . Wrap ( err , "failed to find IPv4 address for interface" )
2022-03-07 10:05:08 +00:00
}
logrus . Infof ( "The interface %s with ipv4 address %s will be used by flannel" , iface . Name , ifaceAddr [ 0 ] )
2022-03-08 08:34:25 +00:00
ifacev6Addr = append ( ifacev6Addr , nil )
case ipv6 :
ifacev6Addr , err = ip . GetInterfaceIP6Addrs ( iface )
if err != nil {
2023-08-31 15:39:33 +00:00
return nil , errors . Wrap ( err , "failed to find IPv6 address for interface" )
2022-03-08 08:34:25 +00:00
}
2022-03-08 08:42:25 +00:00
logrus . Infof ( "The interface %s with ipv6 address %s will be used by flannel" , iface . Name , ifacev6Addr [ 0 ] )
2022-03-07 10:05:08 +00:00
ifaceAddr = append ( ifaceAddr , nil )
2022-03-08 08:34:25 +00:00
case ( ipv4 + ipv6 ) :
ifaceAddr , err = ip . GetInterfaceIP4Addrs ( iface )
if err != nil {
return nil , fmt . Errorf ( "failed to find IPv4 address for interface %s" , iface . Name )
}
2022-03-02 14:01:25 +00:00
ifacev6Addr , err = ip . GetInterfaceIP6Addrs ( iface )
2021-08-17 09:27:54 +00:00
if err != nil {
return nil , fmt . Errorf ( "failed to find IPv6 address for interface %s" , iface . Name )
}
2023-11-08 08:44:38 +00:00
logrus . Infof ( "Using dual-stack mode. The interface %s with ipv4 address %s and ipv6 address %s will be used by flannel" , iface . Name , ifaceAddr [ 0 ] , ifacev6Addr [ 0 ] )
2022-03-08 08:34:25 +00:00
default :
ifaceAddr = append ( ifaceAddr , nil )
2022-03-07 10:05:08 +00:00
ifacev6Addr = append ( ifacev6Addr , nil )
2021-08-17 09:27:54 +00:00
}
2022-03-08 08:34:25 +00:00
2019-01-01 08:23:01 +00:00
if iface . MTU == 0 {
2022-03-07 10:05:08 +00:00
return nil , fmt . Errorf ( "failed to determine MTU for %s interface" , iface . Name )
2019-01-01 08:23:01 +00:00
}
return & backend . ExternalInterface {
2021-08-17 09:27:54 +00:00
Iface : iface ,
2022-03-02 14:01:25 +00:00
IfaceAddr : ifaceAddr [ 0 ] ,
IfaceV6Addr : ifacev6Addr [ 0 ] ,
ExtAddr : ifaceAddr [ 0 ] ,
ExtV6Addr : ifacev6Addr [ 0 ] ,
2019-01-01 08:23:01 +00:00
} , nil
}
2022-03-04 17:06:29 +00:00
func WriteSubnetFile ( path string , nw ip . IP4Net , nwv6 ip . IP6Net , ipMasq bool , bn backend . Network , netMode int ) error {
2019-01-01 08:23:01 +00:00
dir , name := filepath . Split ( path )
os . MkdirAll ( dir , 0755 )
tempFile := filepath . Join ( dir , "." + name )
f , err := os . Create ( tempFile )
if err != nil {
return err
}
// Write out the first usable IP by incrementing
// sn.IP by one
sn := bn . Lease ( ) . Subnet
2019-03-25 04:54:52 +00:00
sn . IP ++
2022-03-04 17:06:29 +00:00
if netMode == ipv4 || netMode == ( ipv4 + ipv6 ) {
fmt . Fprintf ( f , "FLANNEL_NETWORK=%s\n" , nw )
fmt . Fprintf ( f , "FLANNEL_SUBNET=%s\n" , sn )
}
2021-08-17 09:27:54 +00:00
if nwv6 . String ( ) != emptyIPv6Network {
snv6 := bn . Lease ( ) . IPv6Subnet
snv6 . IncrementIP ( )
fmt . Fprintf ( f , "FLANNEL_IPV6_NETWORK=%s\n" , nwv6 )
fmt . Fprintf ( f , "FLANNEL_IPV6_SUBNET=%s\n" , snv6 )
}
2019-01-01 08:23:01 +00:00
fmt . Fprintf ( f , "FLANNEL_MTU=%d\n" , bn . MTU ( ) )
_ , err = fmt . Fprintf ( f , "FLANNEL_IPMASQ=%v\n" , ipMasq )
f . Close ( )
if err != nil {
return err
}
// rename(2) the temporary file to the desired location so that it becomes
// atomically visible with the contents
return os . Rename ( tempFile , path )
//TODO - is this safe? What if it's not on the same FS?
}
2024-05-24 07:52:16 +00:00
// ReadCIDRFromSubnetFile reads the flannel subnet file and extracts the value of IPv4 network CIDRKey
func ReadCIDRFromSubnetFile ( path string , CIDRKey string ) ip . IP4Net {
var prevCIDR ip . IP4Net
if _ , err := os . Stat ( path ) ; ! os . IsNotExist ( err ) {
prevSubnetVals , err := godotenv . Read ( path )
if err != nil {
logrus . Errorf ( "Couldn't fetch previous %s from subnet file at %s: %v" , CIDRKey , path , err )
} else if prevCIDRString , ok := prevSubnetVals [ CIDRKey ] ; ok {
err = prevCIDR . UnmarshalJSON ( [ ] byte ( prevCIDRString ) )
if err != nil {
logrus . Errorf ( "Couldn't parse previous %s from subnet file at %s: %v" , CIDRKey , path , err )
}
}
}
return prevCIDR
}
// ReadIP6CIDRFromSubnetFile reads the flannel subnet file and extracts the value of IPv6 network CIDRKey
func ReadIP6CIDRFromSubnetFile ( path string , CIDRKey string ) ip . IP6Net {
var prevCIDR ip . IP6Net
if _ , err := os . Stat ( path ) ; ! os . IsNotExist ( err ) {
prevSubnetVals , err := godotenv . Read ( path )
if err != nil {
logrus . Errorf ( "Couldn't fetch previous %s from subnet file at %s: %v" , CIDRKey , path , err )
} else if prevCIDRString , ok := prevSubnetVals [ CIDRKey ] ; ok {
err = prevCIDR . UnmarshalJSON ( [ ] byte ( prevCIDRString ) )
if err != nil {
logrus . Errorf ( "Couldn't parse previous %s from subnet file at %s: %v" , CIDRKey , path , err )
}
}
}
return prevCIDR
}