123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342 |
- // Copyright 2015 CoreOS, 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 network
- import (
- "fmt"
- "net"
- "strconv"
- "strings"
- )
- type stanza interface{}
- type stanzaAuto struct {
- interfaces []string
- }
- type stanzaInterface struct {
- name string
- kind interfaceKind
- auto bool
- configMethod configMethod
- options map[string][]string
- }
- type interfaceKind int
- const (
- interfaceBond = interfaceKind(iota)
- interfacePhysical
- interfaceVLAN
- )
- type route struct {
- destination net.IPNet
- gateway net.IP
- }
- type configMethod interface{}
- type configMethodStatic struct {
- addresses []net.IPNet
- nameservers []net.IP
- domains []string
- routes []route
- hwaddress net.HardwareAddr
- }
- type configMethodLoopback struct{}
- type configMethodManual struct{}
- type configMethodDHCP struct {
- hwaddress net.HardwareAddr
- }
- func parseStanzas(lines []string) (stanzas []stanza, err error) {
- rawStanzas, err := splitStanzas(lines)
- if err != nil {
- return nil, err
- }
- stanzas = make([]stanza, 0, len(rawStanzas))
- for _, rawStanza := range rawStanzas {
- if stanza, err := parseStanza(rawStanza); err == nil {
- stanzas = append(stanzas, stanza)
- } else {
- return nil, err
- }
- }
- autos := make([]string, 0)
- interfaceMap := make(map[string]*stanzaInterface)
- for _, stanza := range stanzas {
- switch c := stanza.(type) {
- case *stanzaAuto:
- autos = append(autos, c.interfaces...)
- case *stanzaInterface:
- interfaceMap[c.name] = c
- }
- }
- // Apply the auto attribute
- for _, auto := range autos {
- if iface, ok := interfaceMap[auto]; ok {
- iface.auto = true
- }
- }
- return stanzas, nil
- }
- func splitStanzas(lines []string) ([][]string, error) {
- var curStanza []string
- stanzas := make([][]string, 0)
- for _, line := range lines {
- if isStanzaStart(line) {
- if curStanza != nil {
- stanzas = append(stanzas, curStanza)
- }
- curStanza = []string{line}
- } else if curStanza != nil {
- curStanza = append(curStanza, line)
- } else {
- return nil, fmt.Errorf("missing stanza start %q", line)
- }
- }
- if curStanza != nil {
- stanzas = append(stanzas, curStanza)
- }
- return stanzas, nil
- }
- func isStanzaStart(line string) bool {
- switch strings.Split(line, " ")[0] {
- case "auto":
- fallthrough
- case "iface":
- fallthrough
- case "mapping":
- return true
- }
- if strings.HasPrefix(line, "allow-") {
- return true
- }
- return false
- }
- func parseStanza(rawStanza []string) (stanza, error) {
- if len(rawStanza) == 0 {
- panic("empty stanza")
- }
- tokens := strings.Fields(rawStanza[0])
- if len(tokens) < 2 {
- return nil, fmt.Errorf("malformed stanza start %q", rawStanza[0])
- }
- kind := tokens[0]
- attributes := tokens[1:]
- switch kind {
- case "auto":
- return parseAutoStanza(attributes, rawStanza[1:])
- case "iface":
- return parseInterfaceStanza(attributes, rawStanza[1:])
- default:
- return nil, fmt.Errorf("unknown stanza %q", kind)
- }
- }
- func parseAutoStanza(attributes []string, options []string) (*stanzaAuto, error) {
- return &stanzaAuto{interfaces: attributes}, nil
- }
- func parseInterfaceStanza(attributes []string, options []string) (*stanzaInterface, error) {
- if len(attributes) != 3 {
- return nil, fmt.Errorf("incorrect number of attributes")
- }
- iface := attributes[0]
- confMethod := attributes[2]
- optionMap := make(map[string][]string, 0)
- for _, option := range options {
- if strings.HasPrefix(option, "post-up") {
- tokens := strings.SplitAfterN(option, " ", 2)
- if len(tokens) != 2 {
- continue
- }
- if v, ok := optionMap["post-up"]; ok {
- optionMap["post-up"] = append(v, tokens[1])
- } else {
- optionMap["post-up"] = []string{tokens[1]}
- }
- } else if strings.HasPrefix(option, "pre-down") {
- tokens := strings.SplitAfterN(option, " ", 2)
- if len(tokens) != 2 {
- continue
- }
- if v, ok := optionMap["pre-down"]; ok {
- optionMap["pre-down"] = append(v, tokens[1])
- } else {
- optionMap["pre-down"] = []string{tokens[1]}
- }
- } else {
- tokens := strings.Fields(option)
- optionMap[tokens[0]] = tokens[1:]
- }
- }
- var conf configMethod
- switch confMethod {
- case "static":
- config := configMethodStatic{
- addresses: make([]net.IPNet, 1),
- routes: make([]route, 0),
- nameservers: make([]net.IP, 0),
- }
- if addresses, ok := optionMap["address"]; ok {
- if len(addresses) == 1 {
- config.addresses[0].IP = net.ParseIP(addresses[0])
- }
- }
- if netmasks, ok := optionMap["netmask"]; ok {
- if len(netmasks) == 1 {
- config.addresses[0].Mask = net.IPMask(net.ParseIP(netmasks[0]).To4())
- }
- }
- if config.addresses[0].IP == nil || config.addresses[0].Mask == nil {
- return nil, fmt.Errorf("malformed static network config for %q", iface)
- }
- if gateways, ok := optionMap["gateway"]; ok {
- if len(gateways) == 1 {
- config.routes = append(config.routes, route{
- destination: net.IPNet{
- IP: net.IPv4(0, 0, 0, 0),
- Mask: net.IPv4Mask(0, 0, 0, 0),
- },
- gateway: net.ParseIP(gateways[0]),
- })
- }
- }
- if hwaddress, err := parseHwaddress(optionMap, iface); err == nil {
- config.hwaddress = hwaddress
- } else {
- return nil, err
- }
- for _, nameserver := range optionMap["dns-nameservers"] {
- config.nameservers = append(config.nameservers, net.ParseIP(nameserver))
- }
- for _, postup := range optionMap["post-up"] {
- if strings.HasPrefix(postup, "route add") {
- route := route{}
- fields := strings.Fields(postup)
- for i, field := range fields[:len(fields)-1] {
- switch field {
- case "-net":
- if _, dst, err := net.ParseCIDR(fields[i+1]); err == nil {
- route.destination = *dst
- } else {
- route.destination.IP = net.ParseIP(fields[i+1])
- }
- case "netmask":
- route.destination.Mask = net.IPMask(net.ParseIP(fields[i+1]).To4())
- case "gw":
- route.gateway = net.ParseIP(fields[i+1])
- }
- }
- if route.destination.IP != nil && route.destination.Mask != nil && route.gateway != nil {
- config.routes = append(config.routes, route)
- }
- }
- }
- conf = config
- case "loopback":
- conf = configMethodLoopback{}
- case "manual":
- conf = configMethodManual{}
- case "dhcp":
- config := configMethodDHCP{}
- if hwaddress, err := parseHwaddress(optionMap, iface); err == nil {
- config.hwaddress = hwaddress
- } else {
- return nil, err
- }
- conf = config
- default:
- return nil, fmt.Errorf("invalid config method %q", confMethod)
- }
- if _, ok := optionMap["vlan_raw_device"]; ok {
- return parseVLANStanza(iface, conf, attributes, optionMap)
- }
- if strings.Contains(iface, ".") {
- return parseVLANStanza(iface, conf, attributes, optionMap)
- }
- if _, ok := optionMap["bond-slaves"]; ok {
- return parseBondStanza(iface, conf, attributes, optionMap)
- }
- return parsePhysicalStanza(iface, conf, attributes, optionMap)
- }
- func parseHwaddress(options map[string][]string, iface string) (net.HardwareAddr, error) {
- if hwaddress, ok := options["hwaddress"]; ok && len(hwaddress) == 2 {
- switch hwaddress[0] {
- case "ether":
- if address, err := net.ParseMAC(hwaddress[1]); err == nil {
- return address, nil
- }
- return nil, fmt.Errorf("malformed hwaddress option for %q", iface)
- }
- }
- return nil, nil
- }
- func parseBondStanza(iface string, conf configMethod, attributes []string, options map[string][]string) (*stanzaInterface, error) {
- return &stanzaInterface{name: iface, kind: interfaceBond, configMethod: conf, options: options}, nil
- }
- func parsePhysicalStanza(iface string, conf configMethod, attributes []string, options map[string][]string) (*stanzaInterface, error) {
- return &stanzaInterface{name: iface, kind: interfacePhysical, configMethod: conf, options: options}, nil
- }
- func parseVLANStanza(iface string, conf configMethod, attributes []string, options map[string][]string) (*stanzaInterface, error) {
- var id string
- if strings.Contains(iface, ".") {
- tokens := strings.Split(iface, ".")
- id = tokens[len(tokens)-1]
- } else if strings.HasPrefix(iface, "vlan") {
- id = strings.TrimPrefix(iface, "vlan")
- } else {
- return nil, fmt.Errorf("malformed vlan name %q", iface)
- }
- if _, err := strconv.Atoi(id); err != nil {
- return nil, fmt.Errorf("malformed vlan name %q", iface)
- }
- options["id"] = []string{id}
- options["raw_device"] = options["vlan_raw_device"]
- return &stanzaInterface{name: iface, kind: interfaceVLAN, configMethod: conf, options: options}, nil
- }
|