feat(network): Add support for hwaddress

Currently only supports the ether mode of hwaddress. No immediate plans
to support ax25, ARCnet, or netrom.
This commit is contained in:
Alex Crawford 2014-06-11 23:21:35 -07:00
parent 16d7e8af48
commit 033c8d352f
4 changed files with 110 additions and 28 deletions

View File

@ -48,6 +48,10 @@ func (i *logicalInterface) Network() string {
return config
}
func (i *logicalInterface) Link() string {
return ""
}
type physicalInterface struct {
logicalInterface
}
@ -60,10 +64,6 @@ func (p *physicalInterface) Netdev() string {
return ""
}
func (p *physicalInterface) Link() string {
return ""
}
type bondInterface struct {
logicalInterface
slaves []string
@ -77,10 +77,6 @@ func (b *bondInterface) Netdev() string {
return fmt.Sprintf("[NetDev]\nKind=bond\nName=%s\n", b.name)
}
func (b *bondInterface) Link() string {
return ""
}
type vlanInterface struct {
logicalInterface
id int
@ -92,11 +88,19 @@ func (v *vlanInterface) Name() string {
}
func (v *vlanInterface) Netdev() string {
return fmt.Sprintf("[NetDev]\nKind=vlan\nName=%s\n\n[VLAN]\nId=%d\n", v.name, v.id)
}
func (v *vlanInterface) Link() string {
return ""
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 buildInterfaces(stanzas []*stanzaInterface) []InterfaceGenerator {

View File

@ -143,16 +143,26 @@ func TestVLANInterfaceName(t *testing.T) {
}
func TestVLANInterfaceNetdev(t *testing.T) {
v := vlanInterface{logicalInterface{name: "testname"}, 1, ""}
netdev := `[NetDev]
Kind=vlan
Name=testname
[VLAN]
Id=1
`
if v.Netdev() != netdev {
t.FailNow()
for _, tt := range []struct {
i vlanInterface
l string
}{
{
vlanInterface{logicalInterface{name: "testname"}, 1, ""},
"[NetDev]\nKind=vlan\nName=testname\n\n[VLAN]\nId=1\n",
},
{
vlanInterface{logicalInterface{name: "testname", config: configMethodStatic{hwaddress: net.HardwareAddr([]byte{0, 1, 2, 3, 4, 5})}}, 1, ""},
"[NetDev]\nKind=vlan\nName=testname\nMACAddress=00:01:02:03:04:05\n\n[VLAN]\nId=1\n",
},
{
vlanInterface{logicalInterface{name: "testname", config: configMethodDHCP{hwaddress: net.HardwareAddr([]byte{0, 1, 2, 3, 4, 5})}}, 1, ""},
"[NetDev]\nKind=vlan\nName=testname\nMACAddress=00:01:02:03:04:05\n\n[VLAN]\nId=1\n",
},
} {
if tt.i.Netdev() != tt.l {
t.Fatalf("bad netdev config (%q): got %q, want %q", tt.i, tt.i.Netdev(), tt.l)
}
}
}

View File

@ -40,13 +40,16 @@ type configMethodStatic struct {
address net.IPNet
nameservers []net.IP
routes []route
hwaddress net.HardwareAddr
}
type configMethodLoopback struct{}
type configMethodManual struct{}
type configMethodDHCP struct{}
type configMethodDHCP struct {
hwaddress net.HardwareAddr
}
func parseStanzas(lines []string) (stanzas []stanza, err error) {
rawStanzas, err := splitStanzas(lines)
@ -217,6 +220,11 @@ func parseInterfaceStanza(attributes []string, options []string) (*stanzaInterfa
})
}
}
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))
}
@ -245,7 +253,13 @@ func parseInterfaceStanza(attributes []string, options []string) (*stanzaInterfa
case "manual":
conf = configMethodManual{}
case "dhcp":
conf = configMethodDHCP{}
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)
}
@ -265,6 +279,19 @@ func parseInterfaceStanza(attributes []string, options []string) (*stanzaInterfa
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) {
options["slaves"] = options["bond-slaves"]
return &stanzaInterface{name: iface, kind: interfaceBond, configMethod: conf, options: options}, nil

View File

@ -42,6 +42,8 @@ func TestBadParseInterfaceStanza(t *testing.T) {
{[]string{"eth", "inet", "static"}, []string{"netmask 255.255.255.0"}, "malformed static network config"},
{[]string{"eth", "inet", "static"}, []string{"address invalid", "netmask 255.255.255.0"}, "malformed static network config"},
{[]string{"eth", "inet", "static"}, []string{"address 192.168.1.100", "netmask invalid"}, "malformed static network config"},
{[]string{"eth", "inet", "static"}, []string{"address 192.168.1.100", "netmask 255.255.255.0", "hwaddress ether NotAnAddress"}, "malformed hwaddress option"},
{[]string{"eth", "inet", "dhcp"}, []string{"hwaddress ether NotAnAddress"}, "malformed hwaddress option"},
} {
_, err := parseInterfaceStanza(tt.in, tt.opts)
if err == nil || !strings.HasPrefix(err.Error(), tt.e) {
@ -407,7 +409,46 @@ func TestParseInterfaceStanzaOptions(t *testing.T) {
}
}
func TestParseInterfaceStazaBond(t *testing.T) {
func TestParseInterfaceStanzaHwaddress(t *testing.T) {
for _, tt := range []struct {
attr []string
opt []string
hw net.HardwareAddr
}{
{
[]string{"mybond", "inet", "dhcp"},
[]string{},
nil,
},
{
[]string{"mybond", "inet", "dhcp"},
[]string{"hwaddress ether 00:01:02:03:04:05"},
net.HardwareAddr([]byte{0, 1, 2, 3, 4, 5}),
},
{
[]string{"mybond", "inet", "static"},
[]string{"hwaddress ether 00:01:02:03:04:05", "address 192.168.1.100", "netmask 255.255.255.0"},
net.HardwareAddr([]byte{0, 1, 2, 3, 4, 5}),
},
} {
iface, err := parseInterfaceStanza(tt.attr, tt.opt)
if err != nil {
t.Fatalf("error in parseInterfaceStanza (%q, %q): %q", tt.attr, tt.opt, err)
}
switch c := iface.configMethod.(type) {
case configMethodStatic:
if !reflect.DeepEqual(c.hwaddress, tt.hw) {
t.Fatalf("bad hwaddress (%q, %q): got %q, want %q", tt.attr, tt.opt, c.hwaddress, tt.hw)
}
case configMethodDHCP:
if !reflect.DeepEqual(c.hwaddress, tt.hw) {
t.Fatalf("bad hwaddress (%q, %q): got %q, want %q", tt.attr, tt.opt, c.hwaddress, tt.hw)
}
}
}
}
func TestParseInterfaceStanzaBond(t *testing.T) {
iface, err := parseInterfaceStanza([]string{"mybond", "inet", "manual"}, []string{"bond-slaves eth"})
if err != nil {
t.FailNow()
@ -417,7 +458,7 @@ func TestParseInterfaceStazaBond(t *testing.T) {
}
}
func TestParseInterfaceStazaVLANName(t *testing.T) {
func TestParseInterfaceStanzaVLANName(t *testing.T) {
iface, err := parseInterfaceStanza([]string{"eth0.1", "inet", "manual"}, nil)
if err != nil {
t.FailNow()
@ -427,7 +468,7 @@ func TestParseInterfaceStazaVLANName(t *testing.T) {
}
}
func TestParseInterfaceStazaVLANOption(t *testing.T) {
func TestParseInterfaceStanzaVLANOption(t *testing.T) {
iface, err := parseInterfaceStanza([]string{"vlan1", "inet", "manual"}, []string{"vlan_raw_device eth"})
if err != nil {
t.FailNow()