2021-02-15 00:13:20 +00:00
/ *
Copyright 2021 The Kubernetes Authors All rights reserved .
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 .
* /
2021-02-15 16:00:50 +00:00
package network
2021-02-15 00:13:20 +00:00
import (
"encoding/binary"
"fmt"
"net"
2021-03-17 14:32:39 +00:00
"time"
2021-02-15 00:13:20 +00:00
2022-12-19 02:58:31 +00:00
"github.com/juju/mutex/v2"
2021-02-15 00:13:20 +00:00
"k8s.io/klog/v2"
2022-12-19 02:58:31 +00:00
"k8s.io/minikube/pkg/util/lock"
2021-02-15 00:13:20 +00:00
)
2021-03-17 14:32:39 +00:00
const defaultReservationPeriod = 1 * time . Minute
2021-02-15 16:00:50 +00:00
// Parameters contains main network parameters.
type Parameters struct {
2021-03-22 23:32:50 +00:00
IP string // IP address of network
Netmask string // dotted-decimal format ('a.b.c.d')
Prefix int // network prefix length (number of leading ones in network mask)
CIDR string // CIDR format ('a.b.c.d/n')
Gateway string // taken from network interface address or assumed as first network IP address from given addr
2021-02-15 00:13:20 +00:00
ClientMin string // second IP address
2021-03-22 23:32:50 +00:00
ClientMax string // last IP address before broadcast
2021-02-15 00:13:20 +00:00
Broadcast string // last IP address
2023-01-10 08:01:55 +00:00
IsPrivate bool // whether the IP is private or not
2021-02-15 00:13:20 +00:00
Interface
2022-12-19 02:58:31 +00:00
reservation mutex . Releaser // subnet reservation has lifespan of the process: "If a process dies while the mutex is held, the mutex is automatically released."
2021-02-15 00:13:20 +00:00
}
// Interface contains main network interface parameters.
type Interface struct {
IfaceName string
IfaceIPv4 string
IfaceMTU int
IfaceMAC string
}
2021-06-25 21:26:26 +00:00
// lookupInInterfaces iterates over all local network interfaces
// and tries to match "ip" with associated networks
// returns (network parameters, ip network, nil) if found
2022-08-08 16:29:19 +00:00
//
// (nil, nil, nil) it nof
// (nil, nil, error) if any error happened
2021-06-25 21:26:26 +00:00
func lookupInInterfaces ( ip net . IP ) ( * Parameters , * net . IPNet , error ) {
2021-03-22 23:32:50 +00:00
// check local network interfaces
ifaces , err := net . Interfaces ( )
if err != nil {
2021-06-25 21:26:26 +00:00
return nil , nil , fmt . Errorf ( "failed listing network interfaces: %w" , err )
2021-03-22 23:32:50 +00:00
}
2021-06-24 02:09:14 +00:00
2021-02-15 00:13:20 +00:00
for _ , iface := range ifaces {
2021-06-25 21:26:26 +00:00
2021-02-15 00:13:20 +00:00
ifAddrs , err := iface . Addrs ( )
if err != nil {
2021-06-25 21:26:26 +00:00
return nil , nil , fmt . Errorf ( "failed listing addresses of network interface %+v: %w" , iface , err )
2021-02-15 00:13:20 +00:00
}
2021-06-25 21:26:26 +00:00
2021-02-15 00:13:20 +00:00
for _ , ifAddr := range ifAddrs {
ifip , lan , err := net . ParseCIDR ( ifAddr . String ( ) )
if err != nil {
2021-06-25 21:26:26 +00:00
return nil , nil , fmt . Errorf ( "failed parsing network interface address %+v: %w" , ifAddr , err )
2021-02-15 00:13:20 +00:00
}
if lan . Contains ( ip ) {
2021-06-25 21:26:26 +00:00
ip4 := ifip . To4 ( ) . String ( )
rt := Parameters {
Interface : Interface {
IfaceName : iface . Name ,
IfaceIPv4 : ip4 ,
IfaceMTU : iface . MTU ,
IfaceMAC : iface . HardwareAddr . String ( ) ,
} ,
Gateway : ip4 ,
}
return & rt , lan , nil
2021-02-15 00:13:20 +00:00
}
}
}
2021-06-25 21:26:26 +00:00
return nil , nil , nil
}
// inspect initialises IPv4 network parameters struct from given address addr.
// addr can be single address (like "192.168.17.42"), network address (like "192.168.17.0") or in CIDR form (like "192.168.17.42/24 or "192.168.17.0/24").
// If addr belongs to network of local network interface, parameters will also contain info about that network interface.
2022-11-30 22:46:55 +00:00
var inspect = func ( addr string ) ( * Parameters , error ) {
2021-06-25 21:26:26 +00:00
// extract ip from addr
2022-12-17 19:39:45 +00:00
ip , network , err := ParseAddr ( addr )
2021-06-25 21:26:26 +00:00
if err != nil {
2022-12-17 19:39:45 +00:00
return nil , err
2021-06-25 21:26:26 +00:00
}
n := & Parameters { }
ifParams , ifNet , err := lookupInInterfaces ( ip )
if err != nil {
return nil , err
}
if ifNet != nil {
network = ifNet
n = ifParams
}
2021-02-15 00:13:20 +00:00
2021-03-22 23:32:50 +00:00
// couldn't determine network parameters from addr nor from network interfaces
2021-02-15 00:13:20 +00:00
if network == nil {
ipnet := & net . IPNet {
IP : ip ,
Mask : ip . DefaultMask ( ) , // assume default network mask
}
_ , network , err = net . ParseCIDR ( ipnet . String ( ) )
if err != nil {
2021-03-22 23:32:50 +00:00
return nil , fmt . Errorf ( "failed determining address of network from %s: %w" , addr , err )
2021-02-15 00:13:20 +00:00
}
}
n . IP = network . IP . String ( )
2021-03-22 23:32:50 +00:00
n . Netmask = net . IP ( network . Mask ) . String ( ) // dotted-decimal format ('a.b.c.d')
n . Prefix , _ = network . Mask . Size ( )
2021-02-15 00:13:20 +00:00
n . CIDR = network . String ( )
2023-01-10 08:01:55 +00:00
n . IsPrivate = network . IP . IsPrivate ( )
2021-02-15 00:13:20 +00:00
2021-03-22 23:32:50 +00:00
networkIP := binary . BigEndian . Uint32 ( network . IP ) // IP address of network
2021-02-15 00:13:20 +00:00
networkMask := binary . BigEndian . Uint32 ( network . Mask ) // network mask
broadcastIP := ( networkIP & networkMask ) | ( networkMask ^ 0xffffffff ) // last network IP address
broadcast := make ( net . IP , 4 )
binary . BigEndian . PutUint32 ( broadcast , broadcastIP )
n . Broadcast = broadcast . String ( )
gateway := net . ParseIP ( n . Gateway ) . To4 ( ) // has to be converted to 4-byte representation!
if gateway == nil {
gateway = make ( net . IP , 4 )
binary . BigEndian . PutUint32 ( gateway , networkIP + 1 ) // assume first network IP address
n . Gateway = gateway . String ( )
}
gatewayIP := binary . BigEndian . Uint32 ( gateway )
min := make ( net . IP , 4 )
binary . BigEndian . PutUint32 ( min , gatewayIP + 1 ) // clients-from: first network IP address after gateway
n . ClientMin = min . String ( )
max := make ( net . IP , 4 )
binary . BigEndian . PutUint32 ( max , broadcastIP - 1 ) // clients-from: last network IP address before broadcast
n . ClientMax = max . String ( )
return n , nil
}
2021-02-17 03:03:41 +00:00
// isSubnetTaken returns if local network subnet exists and any error occurred.
2021-02-15 00:13:20 +00:00
// If will return false in case of an error.
2022-11-30 22:46:55 +00:00
var isSubnetTaken = func ( subnet string ) ( bool , error ) {
2021-03-22 23:32:50 +00:00
ifAddrs , err := net . InterfaceAddrs ( )
2021-02-15 00:13:20 +00:00
if err != nil {
2021-03-22 23:32:50 +00:00
return false , fmt . Errorf ( "failed listing network interface addresses: %w" , err )
2021-02-15 00:13:20 +00:00
}
2021-03-22 23:32:50 +00:00
for _ , ifAddr := range ifAddrs {
_ , lan , err := net . ParseCIDR ( ifAddr . String ( ) )
2021-02-15 00:13:20 +00:00
if err != nil {
2021-03-22 23:32:50 +00:00
return false , fmt . Errorf ( "failed parsing network interface address %+v: %w" , ifAddr , err )
2021-02-15 00:13:20 +00:00
}
if lan . Contains ( net . ParseIP ( subnet ) ) {
return true , nil
}
}
return false , nil
}
2023-02-09 20:07:17 +00:00
// IsBuiltinQEMU returns if network is builtin or the legacy value user.
func IsBuiltinQEMU ( network string ) bool {
2023-02-09 18:27:16 +00:00
if network == "user" {
return true
}
2023-02-09 17:48:43 +00:00
return network == "builtin"
2022-09-26 20:58:48 +00:00
}
2021-02-15 16:00:50 +00:00
// FreeSubnet will try to find free private network beginning with startSubnet, incrementing it in steps up to number of tries.
func FreeSubnet ( startSubnet string , step , tries int ) ( * Parameters , error ) {
2022-11-30 22:46:55 +00:00
currSubnet := startSubnet
2021-02-15 00:13:20 +00:00
for try := 0 ; try < tries ; try ++ {
2022-11-30 22:46:55 +00:00
n , err := inspect ( currSubnet )
2021-02-15 00:13:20 +00:00
if err != nil {
return nil , err
}
2022-11-23 00:12:25 +00:00
subnet := n . IP
2023-01-10 08:01:55 +00:00
if n . IsPrivate {
2022-11-23 00:12:25 +00:00
taken , err := isSubnetTaken ( subnet )
2021-02-15 00:13:20 +00:00
if err != nil {
return nil , err
}
if ! taken {
2022-12-19 02:58:31 +00:00
if reservation , err := reserveSubnet ( subnet , defaultReservationPeriod ) ; err == nil {
n . reservation = reservation
2021-03-17 14:32:39 +00:00
klog . Infof ( "using free private subnet %s: %+v" , n . CIDR , n )
return n , nil
}
klog . Infof ( "skipping subnet %s that is reserved: %+v" , n . CIDR , n )
} else {
klog . Infof ( "skipping subnet %s that is taken: %+v" , n . CIDR , n )
2021-02-15 00:13:20 +00:00
}
} else {
klog . Infof ( "skipping subnet %s that is not private" , n . CIDR )
}
2021-03-22 23:32:50 +00:00
prefix , _ := net . ParseIP ( n . IP ) . DefaultMask ( ) . Size ( )
2022-11-30 22:46:55 +00:00
nextSubnet := net . ParseIP ( currSubnet ) . To4 ( )
2021-03-22 23:32:50 +00:00
if prefix <= 16 {
2021-02-15 16:00:50 +00:00
nextSubnet [ 1 ] += byte ( step )
2021-02-15 00:13:20 +00:00
} else {
2021-02-15 16:00:50 +00:00
nextSubnet [ 2 ] += byte ( step )
2021-02-15 00:13:20 +00:00
}
2022-11-30 22:46:55 +00:00
currSubnet = nextSubnet . String ( )
2021-02-15 00:13:20 +00:00
}
2021-02-15 16:43:37 +00:00
return nil , fmt . Errorf ( "no free private network subnets found with given parameters (start: %q, step: %d, tries: %d)" , startSubnet , step , tries )
2021-02-15 00:13:20 +00:00
}
2021-03-17 14:32:39 +00:00
2022-12-17 19:39:45 +00:00
// ParseAddr will try to parse an ip or a cidr address
func ParseAddr ( addr string ) ( net . IP , * net . IPNet , error ) {
ip , network , err := net . ParseCIDR ( addr )
if err != nil {
ip = net . ParseIP ( addr )
if ip == nil {
return nil , nil , fmt . Errorf ( "failed parsing address %s: %w" , addr , err )
}
err = nil
}
return ip , network , err
}
2023-01-10 22:16:51 +00:00
// reserveSubnet returns releaser if subnet was successfully reserved for given period, creating lock for subnet to avoid race condition between multiple minikube instances (especially while testing in parallel).
2022-12-19 02:58:31 +00:00
var reserveSubnet = func ( subnet string , period time . Duration ) ( mutex . Releaser , error ) {
spec := lock . PathMutexSpec ( subnet )
spec . Timeout = 1 * time . Millisecond // practically: just check, don't wait
reservation , err := mutex . Acquire ( spec )
if err != nil {
return nil , err
2021-03-17 14:32:39 +00:00
}
2022-12-19 02:58:31 +00:00
return reservation , nil
2021-03-17 14:32:39 +00:00
}