// 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" "sort" "strconv" "strings" ) type InterfaceGenerator interface { Name() string Filename() string Netdev() string Link() string Network() string Type() string ModprobeParams() string } type networkInterface interface { InterfaceGenerator Children() []networkInterface setConfigDepth(int) } type logicalInterface struct { name string hwaddr net.HardwareAddr config configMethod children []networkInterface configDepth int } func (i *logicalInterface) Name() string { return i.name } func (i *logicalInterface) Network() string { config := fmt.Sprintln("[Match]") if i.name != "" { config += fmt.Sprintf("Name=%s\n", i.name) } if i.hwaddr != nil { config += fmt.Sprintf("MACAddress=%s\n", i.hwaddr) } config += "\n[Network]\n" for _, child := range i.children { switch iface := child.(type) { case *vlanInterface: config += fmt.Sprintf("VLAN=%s\n", iface.name) case *bondInterface: config += fmt.Sprintf("Bond=%s\n", iface.name) } } switch conf := i.config.(type) { case configMethodStatic: if len(conf.domains) > 0 { config += fmt.Sprintf("Domains=%s\n", strings.Join(conf.domains, " ")) } for _, nameserver := range conf.nameservers { config += fmt.Sprintf("DNS=%s\n", nameserver) } for _, addr := range conf.addresses { config += fmt.Sprintf("\n[Address]\nAddress=%s\n", addr.String()) } for _, route := range conf.routes { config += fmt.Sprintf("\n[Route]\nDestination=%s\nGateway=%s\n", route.destination.String(), route.gateway) } case configMethodDHCP: config += "DHCP=true\n" } return config } func (i *logicalInterface) Link() string { return "" } func (i *logicalInterface) Netdev() string { return "" } func (i *logicalInterface) Filename() string { name := i.name if name == "" { name = i.hwaddr.String() } return fmt.Sprintf("%02x-%s", i.configDepth, name) } func (i *logicalInterface) Children() []networkInterface { return i.children } func (i *logicalInterface) ModprobeParams() string { return "" } func (i *logicalInterface) setConfigDepth(depth int) { i.configDepth = depth } type physicalInterface struct { logicalInterface } func (p *physicalInterface) Type() string { return "physical" } type bondInterface struct { logicalInterface slaves []string options map[string]string } func (b *bondInterface) Netdev() string { config := fmt.Sprintf("[NetDev]\nKind=bond\nName=%s\n", b.name) if b.hwaddr != nil { config += fmt.Sprintf("MACAddress=%s\n", b.hwaddr.String()) } config += fmt.Sprintf("\n[Bond]\n") for _, name := range sortedKeys(b.options) { config += fmt.Sprintf("%s=%s\n", name, b.options[name]) } return config } func (b *bondInterface) Type() string { return "bond" } func (b *bondInterface) ModprobeParams() string { params := "" for _, name := range sortedKeys(b.options) { params += fmt.Sprintf("%s=%s ", name, b.options[name]) } params = strings.TrimSuffix(params, " ") return params } type vlanInterface struct { logicalInterface id int rawDevice string } func (v *vlanInterface) Netdev() string { config := fmt.Sprintf("[NetDev]\nKind=vlan\nName=%s\n", v.name) switch c := v.config.(type) { case configMethodStatic: if c.hwaddress != nil { config += fmt.Sprintf("MACAddress=%s\n", c.hwaddress) } case configMethodDHCP: if c.hwaddress != nil { config += fmt.Sprintf("MACAddress=%s\n", c.hwaddress) } } config += fmt.Sprintf("\n[VLAN]\nId=%d\n", v.id) return config } func (v *vlanInterface) Type() string { return "vlan" } func buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator { interfaceMap := createInterfaces(stanzas) linkAncestors(interfaceMap) markConfigDepths(interfaceMap) interfaces := make([]InterfaceGenerator, 0, len(interfaceMap)) for _, name := range sortedInterfaces(interfaceMap) { interfaces = append(interfaces, interfaceMap[name]) } return interfaces } func createInterfaces(stanzas []*stanzaInterface) map[string]networkInterface { interfaceMap := make(map[string]networkInterface) for _, iface := range stanzas { switch iface.kind { case interfaceBond: bondOptions := make(map[string]string) for _, k := range []string{"mode", "miimon", "lacp-rate"} { if v, ok := iface.options["bond-"+k]; ok && len(v) > 0 { bondOptions[k] = v[0] } } interfaceMap[iface.name] = &bondInterface{ logicalInterface{ name: iface.name, config: iface.configMethod, children: []networkInterface{}, }, iface.options["bond-slaves"], bondOptions, } for _, slave := range iface.options["bond-slaves"] { if _, ok := interfaceMap[slave]; !ok { interfaceMap[slave] = &physicalInterface{ logicalInterface{ name: slave, config: configMethodManual{}, children: []networkInterface{}, }, } } } case interfacePhysical: if _, ok := iface.configMethod.(configMethodLoopback); ok { continue } interfaceMap[iface.name] = &physicalInterface{ logicalInterface{ name: iface.name, config: iface.configMethod, children: []networkInterface{}, }, } case interfaceVLAN: var rawDevice string id, _ := strconv.Atoi(iface.options["id"][0]) if device := iface.options["raw_device"]; len(device) == 1 { rawDevice = device[0] if _, ok := interfaceMap[rawDevice]; !ok { interfaceMap[rawDevice] = &physicalInterface{ logicalInterface{ name: rawDevice, config: configMethodManual{}, children: []networkInterface{}, }, } } } interfaceMap[iface.name] = &vlanInterface{ logicalInterface{ name: iface.name, config: iface.configMethod, children: []networkInterface{}, }, id, rawDevice, } } } return interfaceMap } func linkAncestors(interfaceMap map[string]networkInterface) { for _, name := range sortedInterfaces(interfaceMap) { iface := interfaceMap[name] switch i := iface.(type) { case *vlanInterface: if parent, ok := interfaceMap[i.rawDevice]; ok { switch p := parent.(type) { case *physicalInterface: p.children = append(p.children, iface) case *bondInterface: p.children = append(p.children, iface) } } case *bondInterface: for _, slave := range i.slaves { if parent, ok := interfaceMap[slave]; ok { switch p := parent.(type) { case *physicalInterface: p.children = append(p.children, iface) case *bondInterface: p.children = append(p.children, iface) } } } } } } func markConfigDepths(interfaceMap map[string]networkInterface) { rootInterfaceMap := make(map[string]networkInterface) for k, v := range interfaceMap { rootInterfaceMap[k] = v } for _, iface := range interfaceMap { for _, child := range iface.Children() { delete(rootInterfaceMap, child.Name()) } } for _, iface := range rootInterfaceMap { setDepth(iface) } } func setDepth(iface networkInterface) int { maxDepth := 0 for _, child := range iface.Children() { if depth := setDepth(child); depth > maxDepth { maxDepth = depth } } iface.setConfigDepth(maxDepth) return (maxDepth + 1) } func sortedKeys(m map[string]string) (keys []string) { for key := range m { keys = append(keys, key) } sort.Strings(keys) return } func sortedInterfaces(m map[string]networkInterface) (keys []string) { for key := range m { keys = append(keys, key) } sort.Strings(keys) return }