packer-cn/builder/vmware/common/driver_parser.go

1975 lines
54 KiB
Go

package common
import (
"fmt"
"log"
"math"
"net"
"os"
"reflect"
"sort"
"strconv"
"strings"
)
type sentinelSignaller chan struct{}
/** low-level parsing */
// strip the comments and extraneous newlines from a byte channel
func uncomment(eof sentinelSignaller, in <-chan byte) (chan byte, sentinelSignaller) {
out := make(chan byte)
eoc := make(sentinelSignaller)
go func(in <-chan byte, out chan byte) {
var endofline bool
for stillReading := true; stillReading; {
select {
case <-eof:
stillReading = false
case ch := <-in:
switch ch {
case '#':
endofline = true
case '\n':
if endofline {
endofline = false
}
}
if !endofline {
out <- ch
}
}
}
close(eoc)
}(in, out)
return out, eoc
}
// convert a byte channel into a channel of pseudo-tokens
func tokenizeDhcpConfig(eof sentinelSignaller, in chan byte) (chan string, sentinelSignaller) {
var ch byte
var state string
var quote bool
eot := make(sentinelSignaller)
out := make(chan string)
go func(out chan string) {
for stillReading := true; stillReading; {
select {
case <-eof:
stillReading = false
case ch = <-in:
if quote {
if ch == '"' {
out <- state + string(ch)
state, quote = "", false
continue
}
state += string(ch)
continue
}
switch ch {
case '"':
quote = true
state += string(ch)
continue
case '\r':
fallthrough
case '\n':
fallthrough
case '\t':
fallthrough
case ' ':
if len(state) == 0 {
continue
}
out <- state
state = ""
case '{':
fallthrough
case '}':
fallthrough
case ';':
if len(state) > 0 {
out <- state
}
out <- string(ch)
state = ""
default:
state += string(ch)
}
}
}
if len(state) > 0 {
out <- state
}
close(eot)
}(out)
return out, eot
}
/** mid-level parsing */
type tkParameter struct {
name string
operand []string
}
func (e *tkParameter) String() string {
var values []string
for _, val := range e.operand {
values = append(values, val)
}
return fmt.Sprintf("%s [%s]", e.name, strings.Join(values, ","))
}
type tkGroup struct {
parent *tkGroup
id tkParameter
groups []*tkGroup
params []tkParameter
}
func (e *tkGroup) String() string {
var id []string
id = append(id, e.id.name)
for _, val := range e.id.operand {
id = append(id, val)
}
var config []string
for _, val := range e.params {
config = append(config, val.String())
}
return fmt.Sprintf("%s {\n%s\n}", strings.Join(id, " "), strings.Join(config, "\n"))
}
// convert a channel of pseudo-tokens into an tkParameter struct
func parseTokenParameter(in chan string) tkParameter {
var result tkParameter
for {
token := <-in
if result.name == "" {
result.name = token
continue
}
switch token {
case "{":
fallthrough
case "}":
fallthrough
case ";":
goto leave
default:
result.operand = append(result.operand, token)
}
}
leave:
return result
}
// convert a channel of pseudo-tokens into an tkGroup tree */
func parseDhcpConfig(eof sentinelSignaller, in chan string) (tkGroup, error) {
var tokens []string
var result tkGroup
toParameter := func(tokens []string) tkParameter {
out := make(chan string)
go func(out chan string) {
for _, v := range tokens {
out <- v
}
out <- ";"
}(out)
return parseTokenParameter(out)
}
for stillReading, currentgroup := true, &result; stillReading; {
select {
case <-eof:
stillReading = false
case tk := <-in:
switch tk {
case "{":
grp := &tkGroup{parent: currentgroup}
grp.id = toParameter(tokens)
currentgroup.groups = append(currentgroup.groups, grp)
currentgroup = grp
case "}":
if currentgroup.parent == nil {
return tkGroup{}, fmt.Errorf("Unable to close the global declaration")
}
if len(tokens) > 0 {
return tkGroup{}, fmt.Errorf("List of tokens was left unterminated : %v", tokens)
}
currentgroup = currentgroup.parent
case ";":
arg := toParameter(tokens)
currentgroup.params = append(currentgroup.params, arg)
default:
tokens = append(tokens, tk)
continue
}
tokens = []string{}
}
}
return result, nil
}
func tokenizeNetworkMapConfig(eof sentinelSignaller, in chan byte) (chan string, sentinelSignaller) {
var ch byte
var state string
var quote bool
var lastnewline bool
eot := make(sentinelSignaller)
out := make(chan string)
go func(out chan string) {
for stillReading := true; stillReading; {
select {
case <-eof:
stillReading = false
case ch = <-in:
if quote {
if ch == '"' {
out <- state + string(ch)
state, quote = "", false
continue
}
state += string(ch)
continue
}
switch ch {
case '"':
quote = true
state += string(ch)
continue
case '\r':
fallthrough
case '\t':
fallthrough
case ' ':
if len(state) == 0 {
continue
}
out <- state
state = ""
case '\n':
if lastnewline {
continue
}
if len(state) > 0 {
out <- state
}
out <- string(ch)
state = ""
lastnewline = true
continue
case '.':
fallthrough
case '=':
if len(state) > 0 {
out <- state
}
out <- string(ch)
state = ""
default:
state += string(ch)
}
lastnewline = false
}
}
if len(state) > 0 {
out <- state
}
close(eot)
}(out)
return out, eot
}
func parseNetworkMapConfig(eof sentinelSignaller, in chan string) (NetworkMap, error) {
var unsorted map[string]map[string]string
var state []string
addResult := func(network string, attribute string, value string) error {
_, ok := unsorted[network]
if !ok {
unsorted[network] = make(map[string]string)
}
val, err := strconv.Unquote(value)
if err != nil {
return err
}
current := unsorted[network]
current[attribute] = val
return nil
}
stillReading := true
for unsorted = make(map[string]map[string]string); stillReading; {
select {
case <-eof:
if len(state) == 3 {
err := addResult(state[0], state[1], state[2])
if err != nil {
return nil, err
}
}
stillReading = false
case tk := <-in:
switch tk {
case ".":
if len(state) != 1 {
return nil, fmt.Errorf("Missing network index")
}
case "=":
if len(state) != 2 {
return nil, fmt.Errorf("Assignment to empty attribute")
}
case "\n":
if len(state) == 0 {
continue
}
if len(state) != 3 {
return nil, fmt.Errorf("Invalid attribute assignment : %v", state)
}
err := addResult(state[0], state[1], state[2])
if err != nil {
return nil, err
}
state = make([]string, 0)
default:
state = append(state, tk)
}
}
}
result := make([]map[string]string, 0)
var keys []string
for k := range unsorted {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
result = append(result, unsorted[k])
}
return result, nil
}
/** higher-level parsing */
/// parameters
type pParameter interface {
repr() string
}
type pParameterInclude struct {
filename string
}
func (e pParameterInclude) repr() string { return fmt.Sprintf("include-file:filename=%s", e.filename) }
type pParameterOption struct {
name string
value string
}
func (e pParameterOption) repr() string { return fmt.Sprintf("option:%s=%s", e.name, e.value) }
// allow some-kind-of-something
type pParameterGrant struct {
verb string // allow,deny,ignore
attribute string
}
func (e pParameterGrant) repr() string { return fmt.Sprintf("grant:%s,%s", e.verb, e.attribute) }
type pParameterAddress4 []string
func (e pParameterAddress4) repr() string {
return fmt.Sprintf("fixed-address4:%s", strings.Join(e, ","))
}
type pParameterAddress6 []string
func (e pParameterAddress6) repr() string {
return fmt.Sprintf("fixed-address6:%s", strings.Join(e, ","))
}
// hardware address 00:00:00:00:00:00
type pParameterHardware struct {
class string
address []byte
}
func (e pParameterHardware) repr() string {
res := make([]string, 0)
for _, v := range e.address {
res = append(res, fmt.Sprintf("%02x", v))
}
return fmt.Sprintf("hardware-address:%s[%s]", e.class, strings.Join(res, ":"))
}
type pParameterBoolean struct {
parameter string
truancy bool
}
func (e pParameterBoolean) repr() string { return fmt.Sprintf("boolean:%s=%v", e.parameter, e.truancy) }
type pParameterClientMatch struct {
name string
data string
}
func (e pParameterClientMatch) repr() string { return fmt.Sprintf("match-client:%s=%s", e.name, e.data) }
// range 127.0.0.1 127.0.0.255
type pParameterRange4 struct {
min net.IP
max net.IP
}
func (e pParameterRange4) repr() string {
return fmt.Sprintf("range4:%s-%s", e.min.String(), e.max.String())
}
type pParameterRange6 struct {
min net.IP
max net.IP
}
func (e pParameterRange6) repr() string {
return fmt.Sprintf("range6:%s-%s", e.min.String(), e.max.String())
}
type pParameterPrefix6 struct {
min net.IP
max net.IP
bits int
}
func (e pParameterPrefix6) repr() string {
return fmt.Sprintf("prefix6:/%d:%s-%s", e.bits, e.min.String(), e.max.String())
}
// some-kind-of-parameter 1024
type pParameterOther struct {
parameter string
value string
}
func (e pParameterOther) repr() string { return fmt.Sprintf("parameter:%s=%s", e.parameter, e.value) }
type pParameterExpression struct {
parameter string
expression string
}
func (e pParameterExpression) repr() string {
return fmt.Sprintf("parameter-expression:%s=\"%s\"", e.parameter, e.expression)
}
type pDeclarationIdentifier interface {
repr() string
}
type pDeclaration struct {
id pDeclarationIdentifier
parent *pDeclaration
parameters []pParameter
declarations []pDeclaration
}
func (e *pDeclaration) short() string {
return e.id.repr()
}
func (e *pDeclaration) repr() string {
res := e.short()
var parameters []string
for _, v := range e.parameters {
parameters = append(parameters, v.repr())
}
var groups []string
for _, v := range e.declarations {
groups = append(groups, fmt.Sprintf("-> %s", v.short()))
}
if e.parent != nil {
res = fmt.Sprintf("%s parent:%s", res, e.parent.short())
}
return fmt.Sprintf("%s\n%s\n%s\n", res, strings.Join(parameters, "\n"), strings.Join(groups, "\n"))
}
type pDeclarationGlobal struct{}
func (e pDeclarationGlobal) repr() string { return fmt.Sprintf("{global}") }
type pDeclarationShared struct{ name string }
func (e pDeclarationShared) repr() string { return fmt.Sprintf("{shared-network %s}", e.name) }
type pDeclarationSubnet4 struct{ net.IPNet }
func (e pDeclarationSubnet4) repr() string { return fmt.Sprintf("{subnet4 %s}", e.String()) }
type pDeclarationSubnet6 struct{ net.IPNet }
func (e pDeclarationSubnet6) repr() string { return fmt.Sprintf("{subnet6 %s}", e.String()) }
type pDeclarationHost struct{ name string }
func (e pDeclarationHost) repr() string { return fmt.Sprintf("{host name:%s}", e.name) }
type pDeclarationPool struct{}
func (e pDeclarationPool) repr() string { return fmt.Sprintf("{pool}") }
type pDeclarationGroup struct{}
func (e pDeclarationGroup) repr() string { return fmt.Sprintf("{group}") }
type pDeclarationClass struct{ name string }
func (e pDeclarationClass) repr() string { return fmt.Sprintf("{class}") }
/** parsers */
func parseParameter(val tkParameter) (pParameter, error) {
switch val.name {
case "include":
if len(val.operand) != 2 {
return nil, fmt.Errorf("Invalid number of parameters for pParameterInclude : %v", val.operand)
}
name := val.operand[0]
return pParameterInclude{filename: name}, nil
case "option":
if len(val.operand) != 2 {
return nil, fmt.Errorf("Invalid number of parameters for pParameterOption : %v", val.operand)
}
name, value := val.operand[0], val.operand[1]
return pParameterOption{name: name, value: value}, nil
case "allow":
fallthrough
case "deny":
fallthrough
case "ignore":
if len(val.operand) < 1 {
return nil, fmt.Errorf("Invalid number of parameters for pParameterGrant : %v", val.operand)
}
attribute := strings.Join(val.operand, " ")
return pParameterGrant{verb: strings.ToLower(val.name), attribute: attribute}, nil
case "range":
if len(val.operand) < 1 {
return nil, fmt.Errorf("Invalid number of parameters for pParameterRange4 : %v", val.operand)
}
idxAddress := map[bool]int{true: 1, false: 0}[strings.ToLower(val.operand[0]) == "bootp"]
if len(val.operand) > 2+idxAddress {
return nil, fmt.Errorf("Invalid number of parameters for pParameterRange : %v", val.operand)
}
if idxAddress+1 > len(val.operand) {
res := net.ParseIP(val.operand[idxAddress])
return pParameterRange4{min: res, max: res}, nil
}
addr1 := net.ParseIP(val.operand[idxAddress])
addr2 := net.ParseIP(val.operand[idxAddress+1])
return pParameterRange4{min: addr1, max: addr2}, nil
case "range6":
if len(val.operand) == 1 {
address := val.operand[0]
if strings.Contains(address, "/") {
cidr := strings.SplitN(address, "/", 2)
if len(cidr) != 2 {
return nil, fmt.Errorf("Unknown ipv6 format : %v", address)
}
address := net.ParseIP(cidr[0])
bits, err := strconv.Atoi(cidr[1])
if err != nil {
return nil, err
}
mask := net.CIDRMask(bits, net.IPv6len*8)
// figure out the network address
network := address.Mask(mask)
// make a broadcast address
broadcast := network
networkSize, totalSize := mask.Size()
hostSize := totalSize - networkSize
for i := networkSize / 8; i < totalSize/8; i++ {
broadcast[i] = byte(0xff)
}
octetIndex := network[networkSize/8]
bitsLeft := (uint32)(hostSize % 8)
broadcast[octetIndex] = network[octetIndex] | ((1 << bitsLeft) - 1)
// FIXME: check that the broadcast address was made correctly
return pParameterRange6{min: network, max: broadcast}, nil
}
res := net.ParseIP(address)
return pParameterRange6{min: res, max: res}, nil
}
if len(val.operand) == 2 {
addr := net.ParseIP(val.operand[0])
if strings.ToLower(val.operand[1]) == "temporary" {
return pParameterRange6{min: addr, max: addr}, nil
}
other := net.ParseIP(val.operand[1])
return pParameterRange6{min: addr, max: other}, nil
}
return nil, fmt.Errorf("Invalid number of parameters for pParameterRange6 : %v", val.operand)
case "prefix6":
if len(val.operand) != 3 {
return nil, fmt.Errorf("Invalid number of parameters for pParameterRange6 : %v", val.operand)
}
bits, err := strconv.Atoi(val.operand[2])
if err != nil {
return nil, fmt.Errorf("Invalid bits for pParameterPrefix6 : %v", val.operand[2])
}
minaddr := net.ParseIP(val.operand[0])
maxaddr := net.ParseIP(val.operand[1])
return pParameterPrefix6{min: minaddr, max: maxaddr, bits: bits}, nil
case "hardware":
if len(val.operand) != 2 {
return nil, fmt.Errorf("Invalid number of parameters for pParameterHardware : %v", val.operand)
}
class := val.operand[0]
octets := strings.Split(val.operand[1], ":")
address := make([]byte, 0)
for _, v := range octets {
b, err := strconv.ParseInt(v, 16, 0)
if err != nil {
return nil, err
}
address = append(address, byte(b))
}
return pParameterHardware{class: class, address: address}, nil
case "fixed-address":
ip4addrs := make(pParameterAddress4, len(val.operand))
copy(ip4addrs, val.operand)
return ip4addrs, nil
case "fixed-address6":
ip6addrs := make(pParameterAddress6, len(val.operand))
copy(ip6addrs, val.operand)
return ip6addrs, nil
case "host-identifier":
if len(val.operand) != 3 {
return nil, fmt.Errorf("Invalid number of parameters for pParameterClientMatch : %v", val.operand)
}
if val.operand[0] != "option" {
return nil, fmt.Errorf("Invalid match parameter : %v", val.operand[0])
}
optionName := val.operand[1]
optionData := val.operand[2]
return pParameterClientMatch{name: optionName, data: optionData}, nil
default:
length := len(val.operand)
if length < 1 {
return pParameterBoolean{parameter: val.name, truancy: true}, nil
} else if length > 1 {
if val.operand[0] == "=" {
return pParameterExpression{parameter: val.name, expression: strings.Join(val.operand[1:], "")}, nil
}
}
if length != 1 {
return nil, fmt.Errorf("Invalid number of parameters for pParameterOther : %v", val.operand)
}
if strings.ToLower(val.name) == "not" {
return pParameterBoolean{parameter: val.operand[0], truancy: false}, nil
}
return pParameterOther{parameter: val.name, value: val.operand[0]}, nil
}
}
func parseTokenGroup(val tkGroup) (*pDeclaration, error) {
params := val.id.operand
switch val.id.name {
case "group":
return &pDeclaration{id: pDeclarationGroup{}}, nil
case "pool":
return &pDeclaration{id: pDeclarationPool{}}, nil
case "host":
if len(params) == 1 {
return &pDeclaration{id: pDeclarationHost{name: params[0]}}, nil
}
case "subnet":
if len(params) == 3 && strings.ToLower(params[1]) == "netmask" {
addr := make([]byte, 4)
for i, v := range strings.SplitN(params[2], ".", 4) {
res, err := strconv.ParseInt(v, 10, 0)
if err != nil {
return nil, err
}
addr[i] = byte(res)
}
oc1, oc2, oc3, oc4 := addr[0], addr[1], addr[2], addr[3]
if subnet, mask := net.ParseIP(params[0]), net.IPv4Mask(oc1, oc2, oc3, oc4); subnet != nil && mask != nil {
return &pDeclaration{id: pDeclarationSubnet4{net.IPNet{IP: subnet, Mask: mask}}}, nil
}
}
case "subnet6":
if len(params) == 1 {
ip6 := strings.SplitN(params[0], "/", 2)
if len(ip6) == 2 && strings.Contains(ip6[0], ":") {
address := net.ParseIP(ip6[0])
prefix, err := strconv.Atoi(ip6[1])
if err != nil {
return nil, err
}
return &pDeclaration{id: pDeclarationSubnet6{net.IPNet{IP: address, Mask: net.CIDRMask(prefix, net.IPv6len*8)}}}, nil
}
}
case "shared-network":
if len(params) == 1 {
return &pDeclaration{id: pDeclarationShared{name: params[0]}}, nil
}
case "":
return &pDeclaration{id: pDeclarationGlobal{}}, nil
}
return nil, fmt.Errorf("Invalid pDeclaration : %v : %v", val.id.name, params)
}
func flattenDhcpConfig(root tkGroup) (*pDeclaration, error) {
var result *pDeclaration
result, err := parseTokenGroup(root)
if err != nil {
return nil, err
}
for _, p := range root.params {
param, err := parseParameter(p)
if err != nil {
return nil, err
}
result.parameters = append(result.parameters, param)
}
for _, p := range root.groups {
group, err := flattenDhcpConfig(*p)
if err != nil {
return nil, err
}
group.parent = result
result.declarations = append(result.declarations, *group)
}
return result, nil
}
/** reduce the tree into the things that we care about */
type grant uint
const (
ALLOW grant = iota
IGNORE grant = iota
DENY grant = iota
)
type configDeclaration struct {
id []pDeclarationIdentifier
composites []pDeclaration
address []pParameter
options map[string]string
grants map[string]grant
attributes map[string]bool
parameters map[string]string
expressions map[string]string
hostid []pParameterClientMatch
}
func createDeclaration(node pDeclaration) configDeclaration {
var hierarchy []pDeclaration
for n := &node; n != nil; n = n.parent {
hierarchy = append(hierarchy, *n)
}
var result configDeclaration
result.address = make([]pParameter, 0)
result.options = make(map[string]string)
result.grants = make(map[string]grant)
result.attributes = make(map[string]bool)
result.parameters = make(map[string]string)
result.expressions = make(map[string]string)
result.hostid = make([]pParameterClientMatch, 0)
// walk from globals to pDeclaration collecting all parameters
for i := len(hierarchy) - 1; i >= 0; i-- {
result.composites = append(result.composites, hierarchy[(len(hierarchy)-1)-i])
result.id = append(result.id, hierarchy[(len(hierarchy)-1)-i].id)
// update configDeclaration parameters
for _, p := range hierarchy[i].parameters {
switch p.(type) {
case pParameterOption:
result.options[p.(pParameterOption).name] = p.(pParameterOption).value
case pParameterGrant:
Grant := map[string]grant{"ignore": IGNORE, "allow": ALLOW, "deny": DENY}
result.grants[p.(pParameterGrant).attribute] = Grant[p.(pParameterGrant).verb]
case pParameterBoolean:
result.attributes[p.(pParameterBoolean).parameter] = p.(pParameterBoolean).truancy
case pParameterClientMatch:
result.hostid = append(result.hostid, p.(pParameterClientMatch))
case pParameterExpression:
result.expressions[p.(pParameterExpression).parameter] = p.(pParameterExpression).expression
case pParameterOther:
result.parameters[p.(pParameterOther).parameter] = p.(pParameterOther).value
default:
result.address = append(result.address, p)
}
}
}
return result
}
func (e *configDeclaration) repr() string {
var result []string
var res []string
res = make([]string, 0)
for _, v := range e.id {
res = append(res, v.repr())
}
result = append(result, strings.Join(res, ","))
if len(e.address) > 0 {
res = make([]string, 0)
for _, v := range e.address {
res = append(res, v.repr())
}
result = append(result, fmt.Sprintf("address : %v", strings.Join(res, ",")))
}
if len(e.options) > 0 {
result = append(result, fmt.Sprintf("options : %v", e.options))
}
if len(e.grants) > 0 {
result = append(result, fmt.Sprintf("grants : %v", e.grants))
}
if len(e.attributes) > 0 {
result = append(result, fmt.Sprintf("attributes : %v", e.attributes))
}
if len(e.parameters) > 0 {
result = append(result, fmt.Sprintf("parameters : %v", e.parameters))
}
if len(e.expressions) > 0 {
result = append(result, fmt.Sprintf("parameter-expressions : %v", e.expressions))
}
if len(e.hostid) > 0 {
res = make([]string, 0)
for _, v := range e.hostid {
res = append(res, v.repr())
}
result = append(result, fmt.Sprintf("hostid : %v", strings.Join(res, " ")))
}
return strings.Join(result, "\n") + "\n"
}
func (e *configDeclaration) IP4() (net.IP, error) {
var result []string
for _, entry := range e.address {
switch entry.(type) {
case pParameterAddress4:
for _, s := range entry.(pParameterAddress4) {
result = append(result, s)
}
}
}
if len(result) > 1 {
return nil, fmt.Errorf("More than one address4 returned : %v", result)
} else if len(result) == 0 {
return nil, fmt.Errorf("No IP4 addresses found")
}
if res := net.ParseIP(result[0]); res != nil {
return res, nil
}
res, err := net.ResolveIPAddr("ip4", result[0])
if err != nil {
return nil, err
}
return res.IP, nil
}
func (e *configDeclaration) IP6() (net.IP, error) {
var result []string
for _, entry := range e.address {
switch entry.(type) {
case pParameterAddress6:
for _, s := range entry.(pParameterAddress6) {
result = append(result, s)
}
}
}
if len(result) > 1 {
return nil, fmt.Errorf("More than one address6 returned : %v", result)
} else if len(result) == 0 {
return nil, fmt.Errorf("No IP6 addresses found")
}
if res := net.ParseIP(result[0]); res != nil {
return res, nil
}
res, err := net.ResolveIPAddr("ip6", result[0])
if err != nil {
return nil, err
}
return res.IP, nil
}
func (e *configDeclaration) Hardware() (net.HardwareAddr, error) {
var result []pParameterHardware
for _, addr := range e.address {
switch addr.(type) {
case pParameterHardware:
result = append(result, addr.(pParameterHardware))
}
}
if len(result) > 0 {
return nil, fmt.Errorf("More than one hardware address returned : %v", result)
}
res := make(net.HardwareAddr, 0)
for _, by := range result[0].address {
res = append(res, by)
}
return res, nil
}
/*** Dhcp Configuration */
type DhcpConfiguration []configDeclaration
func ReadDhcpConfiguration(fd *os.File) (DhcpConfiguration, error) {
fromfile, eof := consumeFile(fd)
uncommented, eoc := uncomment(eof, fromfile)
tokenized, eot := tokenizeDhcpConfig(eoc, uncommented)
parsetree, err := parseDhcpConfig(eot, tokenized)
if err != nil {
return nil, err
}
global, err := flattenDhcpConfig(parsetree)
if err != nil {
return nil, err
}
var walkDeclarations func(root pDeclaration, out chan *configDeclaration)
walkDeclarations = func(root pDeclaration, out chan *configDeclaration) {
res := createDeclaration(root)
out <- &res
for _, p := range root.declarations {
walkDeclarations(p, out)
}
}
each := make(chan *configDeclaration)
go func(out chan *configDeclaration) {
walkDeclarations(*global, out)
out <- nil
}(each)
var result DhcpConfiguration
for decl := <-each; decl != nil; decl = <-each {
result = append(result, *decl)
}
return result, nil
}
func (e *DhcpConfiguration) Global() configDeclaration {
result := (*e)[0]
if len(result.id) != 1 {
panic(fmt.Errorf("Something that can't happen happened"))
}
return result
}
func (e *DhcpConfiguration) SubnetByAddress(address net.IP) (configDeclaration, error) {
var result []configDeclaration
for _, entry := range *e {
switch entry.id[0].(type) {
case pDeclarationSubnet4:
id := entry.id[0].(pDeclarationSubnet4)
if id.Contains(address) {
result = append(result, entry)
}
case pDeclarationSubnet6:
id := entry.id[0].(pDeclarationSubnet6)
if id.Contains(address) {
result = append(result, entry)
}
}
}
if len(result) == 0 {
return configDeclaration{}, fmt.Errorf("No network declarations containing %s found", address.String())
}
if len(result) > 1 {
return configDeclaration{}, fmt.Errorf("More than 1 network declaration found : %v", result)
}
return result[0], nil
}
func (e *DhcpConfiguration) HostByName(host string) (configDeclaration, error) {
var result []configDeclaration
for _, entry := range *e {
switch entry.id[0].(type) {
case pDeclarationHost:
id := entry.id[0].(pDeclarationHost)
if strings.ToLower(id.name) == strings.ToLower(host) {
result = append(result, entry)
}
}
}
if len(result) == 0 {
return configDeclaration{}, fmt.Errorf("No host declarations containing %s found", host)
}
if len(result) > 1 {
return configDeclaration{}, fmt.Errorf("More than 1 host declaration found : %v", result)
}
return result[0], nil
}
/*** Network Map */
type NetworkMap []map[string]string
type NetworkNameMapper interface {
NameIntoDevices(string) ([]string, error)
DeviceIntoName(string) (string, error)
}
func ReadNetworkMap(fd *os.File) (NetworkMap, error) {
fromfile, eof := consumeFile(fd)
uncommented, eoc := uncomment(eof, fromfile)
tokenized, eot := tokenizeNetworkMapConfig(eoc, uncommented)
result, err := parseNetworkMapConfig(eot, tokenized)
if err != nil {
return nil, err
}
return result, nil
}
func (e NetworkMap) NameIntoDevices(name string) ([]string, error) {
var devices []string
for _, val := range e {
if strings.ToLower(val["name"]) == strings.ToLower(name) {
devices = append(devices, val["device"])
}
}
if len(devices) > 0 {
return devices, nil
}
return make([]string, 0), fmt.Errorf("Network name not found : %v", name)
}
func (e NetworkMap) DeviceIntoName(device string) (string, error) {
for _, val := range e {
if strings.ToLower(val["device"]) == strings.ToLower(device) {
return val["name"], nil
}
}
return "", fmt.Errorf("Device name not found : %v", device)
}
func (e *NetworkMap) repr() string {
var result []string
for idx, val := range *e {
result = append(result, fmt.Sprintf("network%d.name = \"%s\"", idx, val["name"]))
result = append(result, fmt.Sprintf("network%d.device = \"%s\"", idx, val["device"]))
}
return strings.Join(result, "\n")
}
/*** parser for VMware Fusion's networking file */
func tokenizeNetworkingConfig(eof sentinelSignaller, in chan byte) (chan string, sentinelSignaller) {
var ch byte
var state string
var repeat_newline bool
eot := make(sentinelSignaller)
out := make(chan string)
go func(out chan string) {
for reading := true; reading; {
select {
case <-eof:
reading = false
case ch = <-in:
switch ch {
case '\r':
fallthrough
case '\t':
fallthrough
case ' ':
if len(state) == 0 {
continue
}
out <- state
state = ""
case '\n':
if repeat_newline {
continue
}
if len(state) > 0 {
out <- state
}
out <- string(ch)
state = ""
repeat_newline = true
continue
default:
state += string(ch)
}
repeat_newline = false
}
}
if len(state) > 0 {
out <- state
}
close(eot)
}(out)
return out, eot
}
func splitNetworkingConfig(eof sentinelSignaller, in chan string) (chan []string, sentinelSignaller) {
var out chan []string
eos := make(sentinelSignaller)
out = make(chan []string)
go func(out chan []string) {
row := make([]string, 0)
for reading := true; reading; {
select {
case <-eof:
reading = false
case tk := <-in:
switch tk {
case "\n":
if len(row) > 0 {
out <- row
}
row = make([]string, 0)
default:
row = append(row, tk)
}
}
}
if len(row) > 0 {
out <- row
}
close(eos)
}(out)
return out, eos
}
/// All token types in networking file.
// VERSION token
type networkingVERSION struct {
value string
}
func networkingReadVersion(row []string) (*networkingVERSION, error) {
if len(row) != 1 {
return nil, fmt.Errorf("Unexpected format for VERSION entry : %v", row)
}
res := &networkingVERSION{value: row[0]}
if !res.Valid() {
return nil, fmt.Errorf("Unexpected format for VERSION entry : %v", row)
}
return res, nil
}
func (s networkingVERSION) Repr() string {
if !s.Valid() {
return fmt.Sprintf("VERSION{INVALID=\"%v\"}", s.value)
}
return fmt.Sprintf("VERSION{%f}", s.Number())
}
func (s networkingVERSION) Valid() bool {
tokens := strings.SplitN(s.value, "=", 2)
if len(tokens) != 2 || tokens[0] != "VERSION" {
return false
}
tokens = strings.Split(tokens[1], ",")
if len(tokens) != 2 {
return false
}
for _, t := range tokens {
_, err := strconv.ParseUint(t, 10, 64)
if err != nil {
return false
}
}
return true
}
func (s networkingVERSION) Number() float64 {
var result float64
tokens := strings.SplitN(s.value, "=", 2)
tokens = strings.Split(tokens[1], ",")
integer, err := strconv.ParseUint(tokens[0], 10, 64)
if err != nil {
integer = 0
}
result = float64(integer)
mantissa, err := strconv.ParseUint(tokens[1], 10, 64)
if err != nil {
return result
}
denomination := math.Pow(10.0, float64(len(tokens[1])))
return result + (float64(mantissa) / denomination)
}
// VNET_X token
type networkingVNET struct {
value string
}
func (s networkingVNET) Valid() bool {
if strings.ToUpper(s.value) != s.value {
return false
}
tokens := strings.SplitN(s.value, "_", 3)
if len(tokens) != 3 || tokens[0] != "VNET" {
return false
}
_, err := strconv.ParseUint(tokens[1], 10, 64)
if err != nil {
return false
}
return true
}
func (s networkingVNET) Number() int {
tokens := strings.SplitN(s.value, "_", 3)
res, err := strconv.Atoi(tokens[1])
if err != nil {
return ^int(0)
}
return res
}
func (s networkingVNET) Option() string {
tokens := strings.SplitN(s.value, "_", 3)
if len(tokens) == 3 {
return tokens[2]
}
return ""
}
func (s networkingVNET) Repr() string {
if !s.Valid() {
tokens := strings.SplitN(s.value, "_", 3)
return fmt.Sprintf("VNET{INVALID=%v}", tokens)
}
return fmt.Sprintf("VNET{%d} %s", s.Number(), s.Option())
}
// Interface name
type networkingInterface struct {
name string
}
func (s networkingInterface) Interface() (*net.Interface, error) {
return net.InterfaceByName(s.name)
}
// networking command entry types
type networkingCommandEntry_answer struct {
vnet networkingVNET
value string
}
type networkingCommandEntry_remove_answer struct {
vnet networkingVNET
}
type networkingCommandEntry_add_nat_portfwd struct {
vnet int
protocol string
port int
target_host net.IP
target_port int
}
type networkingCommandEntry_remove_nat_portfwd struct {
vnet int
protocol string
port int
}
type networkingCommandEntry_add_dhcp_mac_to_ip struct {
vnet int
mac net.HardwareAddr
ip net.IP
}
type networkingCommandEntry_remove_dhcp_mac_to_ip struct {
vnet int
mac net.HardwareAddr
}
type networkingCommandEntry_add_bridge_mapping struct {
intf networkingInterface
vnet int
}
type networkingCommandEntry_remove_bridge_mapping struct {
intf networkingInterface
}
type networkingCommandEntry_add_nat_prefix struct {
vnet int
prefix int
}
type networkingCommandEntry_remove_nat_prefix struct {
vnet int
prefix int
}
type networkingCommandEntry struct {
entry interface{}
answer *networkingCommandEntry_answer
remove_answer *networkingCommandEntry_remove_answer
add_nat_portfwd *networkingCommandEntry_add_nat_portfwd
remove_nat_portfwd *networkingCommandEntry_remove_nat_portfwd
add_dhcp_mac_to_ip *networkingCommandEntry_add_dhcp_mac_to_ip
remove_dhcp_mac_to_ip *networkingCommandEntry_remove_dhcp_mac_to_ip
add_bridge_mapping *networkingCommandEntry_add_bridge_mapping
remove_bridge_mapping *networkingCommandEntry_remove_bridge_mapping
add_nat_prefix *networkingCommandEntry_add_nat_prefix
remove_nat_prefix *networkingCommandEntry_remove_nat_prefix
}
func (e networkingCommandEntry) Name() string {
switch e.entry.(type) {
case networkingCommandEntry_answer:
return "answer"
case networkingCommandEntry_remove_answer:
return "remove_answer"
case networkingCommandEntry_add_nat_portfwd:
return "add_nat_portfwd"
case networkingCommandEntry_remove_nat_portfwd:
return "remove_nat_portfwd"
case networkingCommandEntry_add_dhcp_mac_to_ip:
return "add_dhcp_mac_to_ip"
case networkingCommandEntry_remove_dhcp_mac_to_ip:
return "remove_dhcp_mac_to_ip"
case networkingCommandEntry_add_bridge_mapping:
return "add_bridge_mapping"
case networkingCommandEntry_remove_bridge_mapping:
return "remove_bridge_mapping"
case networkingCommandEntry_add_nat_prefix:
return "add_nat_prefix"
case networkingCommandEntry_remove_nat_prefix:
return "remove_nat_prefix"
}
return ""
}
func (e networkingCommandEntry) Entry() reflect.Value {
this := reflect.ValueOf(e)
switch e.entry.(type) {
case networkingCommandEntry_answer:
return reflect.Indirect(this.FieldByName("answer"))
case networkingCommandEntry_remove_answer:
return reflect.Indirect(this.FieldByName("remove_answer"))
case networkingCommandEntry_add_nat_portfwd:
return reflect.Indirect(this.FieldByName("add_nat_portfwd"))
case networkingCommandEntry_remove_nat_portfwd:
return reflect.Indirect(this.FieldByName("remove_nat_portfwd"))
case networkingCommandEntry_add_dhcp_mac_to_ip:
return reflect.Indirect(this.FieldByName("add_dhcp_mac_to_ip"))
case networkingCommandEntry_remove_dhcp_mac_to_ip:
return reflect.Indirect(this.FieldByName("remove_dhcp_mac_to_ip"))
case networkingCommandEntry_add_bridge_mapping:
return reflect.Indirect(this.FieldByName("add_bridge_mapping"))
case networkingCommandEntry_remove_bridge_mapping:
return reflect.Indirect(this.FieldByName("remove_bridge_mapping"))
case networkingCommandEntry_add_nat_prefix:
return reflect.Indirect(this.FieldByName("add_nat_prefix"))
case networkingCommandEntry_remove_nat_prefix:
return reflect.Indirect(this.FieldByName("remove_nat_prefix"))
}
return reflect.Value{}
}
func (e networkingCommandEntry) Repr() string {
var result map[string]interface{}
result = make(map[string]interface{})
entryN, entry := e.Name(), e.Entry()
entryT := entry.Type()
for i := 0; i < entry.NumField(); i++ {
fld, fldT := entry.Field(i), entryT.Field(i)
result[fldT.Name] = fld
}
return fmt.Sprintf("%s -> %v", entryN, result)
}
// networking command entry parsers
func parseNetworkingCommand_answer(row []string) (*networkingCommandEntry, error) {
if len(row) != 2 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 2, len(row))
}
vnet := networkingVNET{value: row[0]}
if !vnet.Valid() {
return nil, fmt.Errorf("Invalid format for VNET.")
}
value := row[1]
result := networkingCommandEntry_answer{vnet: vnet, value: value}
return &networkingCommandEntry{entry: result, answer: &result}, nil
}
func parseNetworkingCommand_remove_answer(row []string) (*networkingCommandEntry, error) {
if len(row) != 1 {
return nil, fmt.Errorf("Expected %d argument. Received %d.", 1, len(row))
}
vnet := networkingVNET{value: row[0]}
if !vnet.Valid() {
return nil, fmt.Errorf("Invalid format for VNET.")
}
result := networkingCommandEntry_remove_answer{vnet: vnet}
return &networkingCommandEntry{entry: result, remove_answer: &result}, nil
}
func parseNetworkingCommand_add_nat_portfwd(row []string) (*networkingCommandEntry, error) {
if len(row) != 5 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 5, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
protocol := strings.ToLower(row[1])
if !(protocol == "tcp" || protocol == "udp") {
return nil, fmt.Errorf("Expected \"tcp\" or \"udp\" for second argument. : %v", row[1])
}
sport, err := strconv.Atoi(row[2])
if err != nil {
return nil, fmt.Errorf("Unable to parse third argument as an integer. : %v", row[2])
}
dest := net.ParseIP(row[3])
if dest == nil {
return nil, fmt.Errorf("Unable to parse fourth argument as an IPv4 address. : %v", row[2])
}
dport, err := strconv.Atoi(row[4])
if err != nil {
return nil, fmt.Errorf("Unable to parse fifth argument as an integer. : %v", row[4])
}
result := networkingCommandEntry_add_nat_portfwd{vnet: vnet - 1, protocol: protocol, port: sport, target_host: dest, target_port: dport}
return &networkingCommandEntry{entry: result, add_nat_portfwd: &result}, nil
}
func parseNetworkingCommand_remove_nat_portfwd(row []string) (*networkingCommandEntry, error) {
if len(row) != 3 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 3, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
protocol := strings.ToLower(row[1])
if !(protocol == "tcp" || protocol == "udp") {
return nil, fmt.Errorf("Expected \"tcp\" or \"udp\" for second argument. : %v", row[1])
}
sport, err := strconv.Atoi(row[2])
if err != nil {
return nil, fmt.Errorf("Unable to parse third argument as an integer. : %v", row[2])
}
result := networkingCommandEntry_remove_nat_portfwd{vnet: vnet - 1, protocol: protocol, port: sport}
return &networkingCommandEntry{entry: result, remove_nat_portfwd: &result}, nil
}
func parseNetworkingCommand_add_dhcp_mac_to_ip(row []string) (*networkingCommandEntry, error) {
if len(row) != 3 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 3, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
mac, err := net.ParseMAC(row[1])
if err != nil {
return nil, fmt.Errorf("Unable to parse second argument as hwaddr. : %v", row[1])
}
ip := net.ParseIP(row[2])
if ip != nil {
return nil, fmt.Errorf("Unable to parse third argument as ipv4. : %v", row[2])
}
result := networkingCommandEntry_add_dhcp_mac_to_ip{vnet: vnet - 1, mac: mac, ip: ip}
return &networkingCommandEntry{entry: result, add_dhcp_mac_to_ip: &result}, nil
}
func parseNetworkingCommand_remove_dhcp_mac_to_ip(row []string) (*networkingCommandEntry, error) {
if len(row) != 2 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 2, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
mac, err := net.ParseMAC(row[1])
if err != nil {
return nil, fmt.Errorf("Unable to parse second argument as hwaddr. : %v", row[1])
}
result := networkingCommandEntry_remove_dhcp_mac_to_ip{vnet: vnet - 1, mac: mac}
return &networkingCommandEntry{entry: result, remove_dhcp_mac_to_ip: &result}, nil
}
func parseNetworkingCommand_add_bridge_mapping(row []string) (*networkingCommandEntry, error) {
if len(row) != 2 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 2, len(row))
}
intf := networkingInterface{name: row[0]}
vnet, err := strconv.Atoi(row[1])
if err != nil {
return nil, fmt.Errorf("Unable to parse second argument as an integer. : %v", row[2])
}
result := networkingCommandEntry_add_bridge_mapping{intf: intf, vnet: vnet - 1}
return &networkingCommandEntry{entry: result, add_bridge_mapping: &result}, nil
}
func parseNetworkingCommand_remove_bridge_mapping(row []string) (*networkingCommandEntry, error) {
if len(row) != 1 {
return nil, fmt.Errorf("Expected %d argument. Received %d.", 1, len(row))
}
intf := networkingInterface{name: row[0]}
/*
number, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
*/
result := networkingCommandEntry_remove_bridge_mapping{intf: intf}
return &networkingCommandEntry{entry: result, remove_bridge_mapping: &result}, nil
}
func parseNetworkingCommand_add_nat_prefix(row []string) (*networkingCommandEntry, error) {
if len(row) != 2 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 2, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
if !strings.HasPrefix(row[1], "/") {
return nil, fmt.Errorf("Expected second argument to begin with \"/\". : %v", row[1])
}
prefix, err := strconv.Atoi(row[1][1:])
if err != nil {
return nil, fmt.Errorf("Unable to parse prefix out of second argument. : %v", row[1])
}
result := networkingCommandEntry_add_nat_prefix{vnet: vnet - 1, prefix: prefix}
return &networkingCommandEntry{entry: result, add_nat_prefix: &result}, nil
}
func parseNetworkingCommand_remove_nat_prefix(row []string) (*networkingCommandEntry, error) {
if len(row) != 2 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 2, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
if !strings.HasPrefix(row[1], "/") {
return nil, fmt.Errorf("Expected second argument to begin with \"/\". : %v", row[1])
}
prefix, err := strconv.Atoi(row[1][1:])
if err != nil {
return nil, fmt.Errorf("Unable to parse prefix out of second argument. : %v", row[1])
}
result := networkingCommandEntry_remove_nat_prefix{vnet: vnet - 1, prefix: prefix}
return &networkingCommandEntry{entry: result, remove_nat_prefix: &result}, nil
}
type networkingCommandParser struct {
command string
callback func([]string) (*networkingCommandEntry, error)
}
var NetworkingCommandParsers = []networkingCommandParser{
/* DictRecordParseFunct */ {command: "answer", callback: parseNetworkingCommand_answer},
/* DictRecordParseFunct */ {command: "remove_answer", callback: parseNetworkingCommand_remove_answer},
/* NatFwdRecordParseFunct */ {command: "add_nat_portfwd", callback: parseNetworkingCommand_add_nat_portfwd},
/* NatFwdRecordParseFunct */ {command: "remove_nat_portfwd", callback: parseNetworkingCommand_remove_nat_portfwd},
/* DhcpMacRecordParseFunct */ {command: "add_dhcp_mac_to_ip", callback: parseNetworkingCommand_add_dhcp_mac_to_ip},
/* DhcpMacRecordParseFunct */ {command: "remove_dhcp_mac_to_ip", callback: parseNetworkingCommand_remove_dhcp_mac_to_ip},
/* BridgeMappingRecordParseFunct */ {command: "add_bridge_mapping", callback: parseNetworkingCommand_add_bridge_mapping},
/* BridgeMappingRecordParseFunct */ {command: "remove_bridge_mapping", callback: parseNetworkingCommand_remove_bridge_mapping},
/* NatPrefixRecordParseFunct */ {command: "add_nat_prefix", callback: parseNetworkingCommand_add_nat_prefix},
/* NatPrefixRecordParseFunct */ {command: "remove_nat_prefix", callback: parseNetworkingCommand_remove_nat_prefix},
}
func NetworkingParserByCommand(command string) *func([]string) (*networkingCommandEntry, error) {
for _, p := range NetworkingCommandParsers {
if p.command == command {
return &p.callback
}
}
return nil
}
func parseNetworkingConfig(eof sentinelSignaller, rows chan []string) (chan networkingCommandEntry, sentinelSignaller) {
var out chan networkingCommandEntry
eop := make(sentinelSignaller)
out = make(chan networkingCommandEntry)
go func(in chan []string, out chan networkingCommandEntry) {
for reading := true; reading; {
select {
case <-eof:
reading = false
case row := <-in:
if len(row) >= 1 {
parser := NetworkingParserByCommand(row[0])
if parser == nil {
log.Printf("Invalid command : %v", row)
continue
}
callback := *parser
entry, err := callback(row[1:])
if err != nil {
log.Printf("Unable to parse command : %v %v", err, row)
continue
}
out <- *entry
}
}
}
close(eop)
}(rows, out)
return out, eop
}
type NetworkingConfig struct {
answer map[int]map[string]string
nat_portfwd map[int]map[string]string
dhcp_mac_to_ip map[int]map[string]net.IP
//bridge_mapping map[net.Interface]uint64 // XXX: we don't need the actual interface for anything but informing the user.
bridge_mapping map[string]int
nat_prefix map[int][]int
}
func (c NetworkingConfig) repr() string {
return fmt.Sprintf("answer -> %v\nnat_portfwd -> %v\ndhcp_mac_to_ip -> %v\nbridge_mapping -> %v\nnat_prefix -> %v", c.answer, c.nat_portfwd, c.dhcp_mac_to_ip, c.bridge_mapping, c.nat_prefix)
}
func flattenNetworkingConfig(eof sentinelSignaller, in chan networkingCommandEntry) NetworkingConfig {
var result NetworkingConfig
var vmnet int
result.answer = make(map[int]map[string]string)
result.nat_portfwd = make(map[int]map[string]string)
result.dhcp_mac_to_ip = make(map[int]map[string]net.IP)
result.bridge_mapping = make(map[string]int)
result.nat_prefix = make(map[int][]int)
for reading := true; reading; {
select {
case <-eof:
reading = false
case e := <-in:
switch e.entry.(type) {
case networkingCommandEntry_answer:
vnet := e.answer.vnet
answers, exists := result.answer[vnet.Number()]
if !exists {
answers = make(map[string]string)
result.answer[vnet.Number()] = answers
}
answers[vnet.Option()] = e.answer.value
case networkingCommandEntry_remove_answer:
vnet := e.remove_answer.vnet
answers, exists := result.answer[vnet.Number()]
if exists {
delete(answers, vnet.Option())
} else {
log.Printf("Unable to remove answer %s as specified by `remove_answer`.\n", vnet.Repr())
}
case networkingCommandEntry_add_nat_portfwd:
vmnet = e.add_nat_portfwd.vnet
protoport := fmt.Sprintf("%s/%d", e.add_nat_portfwd.protocol, e.add_nat_portfwd.port)
target := fmt.Sprintf("%s:%d", e.add_nat_portfwd.target_host, e.add_nat_portfwd.target_port)
portfwds, exists := result.nat_portfwd[vmnet]
if !exists {
portfwds = make(map[string]string)
result.nat_portfwd[vmnet] = portfwds
}
portfwds[protoport] = target
case networkingCommandEntry_remove_nat_portfwd:
vmnet = e.remove_nat_portfwd.vnet
protoport := fmt.Sprintf("%s/%d", e.remove_nat_portfwd.protocol, e.remove_nat_portfwd.port)
portfwds, exists := result.nat_portfwd[vmnet]
if exists {
delete(portfwds, protoport)
} else {
log.Printf("Unable to remove nat port-forward %s from interface %s%d as requested by `remove_nat_portfwd`.\n", protoport, NetworkingInterfacePrefix, vmnet)
}
case networkingCommandEntry_add_dhcp_mac_to_ip:
vmnet = e.add_dhcp_mac_to_ip.vnet
dhcpmacs, exists := result.dhcp_mac_to_ip[vmnet]
if !exists {
dhcpmacs = make(map[string]net.IP)
result.dhcp_mac_to_ip[vmnet] = dhcpmacs
}
dhcpmacs[e.add_dhcp_mac_to_ip.mac.String()] = e.add_dhcp_mac_to_ip.ip
case networkingCommandEntry_remove_dhcp_mac_to_ip:
vmnet = e.remove_dhcp_mac_to_ip.vnet
dhcpmacs, exists := result.dhcp_mac_to_ip[vmnet]
if exists {
delete(dhcpmacs, e.remove_dhcp_mac_to_ip.mac.String())
} else {
log.Printf("Unable to remove dhcp_mac_to_ip entry %v from interface %s%d as specified by `remove_dhcp_mac_to_ip`.\n", e.remove_dhcp_mac_to_ip, NetworkingInterfacePrefix, vmnet)
}
case networkingCommandEntry_add_bridge_mapping:
intf := e.add_bridge_mapping.intf
if _, err := intf.Interface(); err != nil {
log.Printf("Interface \"%s\" as specified by `add_bridge_mapping` was not found on the current platform. This is a non-critical error. Ignoring.", intf.name)
}
result.bridge_mapping[intf.name] = e.add_bridge_mapping.vnet
case networkingCommandEntry_remove_bridge_mapping:
intf := e.remove_bridge_mapping.intf
if _, err := intf.Interface(); err != nil {
log.Printf("Interface \"%s\" as specified by `remove_bridge_mapping` was not found on the current platform. This is a non-critical error. Ignoring.", intf.name)
}
delete(result.bridge_mapping, intf.name)
case networkingCommandEntry_add_nat_prefix:
vmnet = e.add_nat_prefix.vnet
_, exists := result.nat_prefix[vmnet]
if exists {
result.nat_prefix[vmnet] = append(result.nat_prefix[vmnet], e.add_nat_prefix.prefix)
} else {
result.nat_prefix[vmnet] = []int{e.add_nat_prefix.prefix}
}
case networkingCommandEntry_remove_nat_prefix:
vmnet = e.remove_nat_prefix.vnet
prefixes, exists := result.nat_prefix[vmnet]
if exists {
for index := 0; index < len(prefixes); index++ {
if prefixes[index] == e.remove_nat_prefix.prefix {
result.nat_prefix[vmnet] = append(prefixes[:index], prefixes[index+1:]...)
break
}
}
} else {
log.Printf("Unable to remove nat prefix /%d from interface %s%d as specified by `remove_nat_prefix`.\n", e.remove_nat_prefix.prefix, NetworkingInterfacePrefix, vmnet)
}
}
}
}
return result
}
// Constructor for networking file
func ReadNetworkingConfig(fd *os.File) (NetworkingConfig, error) {
// start piecing together different parts of the file
fromfile, eof := consumeFile(fd)
tokenized, eot := tokenizeNetworkingConfig(eof, fromfile)
rows, eos := splitNetworkingConfig(eot, tokenized)
entries, eop := parseNetworkingConfig(eos, rows)
// parse the version
parsed_version, err := networkingReadVersion(<-rows)
if err != nil {
return NetworkingConfig{}, err
}
// verify that it's 1.0 since that's all we support.
version := parsed_version.Number()
if version != 1.0 {
return NetworkingConfig{}, fmt.Errorf("Expected version %f of networking file. Received version %f.", 1.0, version)
}
// convert to a configuration
result := flattenNetworkingConfig(eop, entries)
return result, nil
}
// netmapper interface
type NetworkingType int
const (
NetworkingType_HOSTONLY = iota + 1
NetworkingType_NAT
NetworkingType_BRIDGED
)
func networkingConfig_InterfaceTypes(config NetworkingConfig) map[int]NetworkingType {
var result map[int]NetworkingType
result = make(map[int]NetworkingType)
// defaults
result[0] = NetworkingType_BRIDGED
result[1] = NetworkingType_HOSTONLY
result[8] = NetworkingType_NAT
// walk through config collecting bridged interfaces
for _, vmnet := range config.bridge_mapping {
result[vmnet] = NetworkingType_BRIDGED
}
// walk through answers finding out which ones are nat versus hostonly
for vmnet, table := range config.answer {
// everything should be defined as a virtual adapter...
if table["VIRTUAL_ADAPTER"] == "yes" {
// validate that the VNET entry contains everything we expect it to
_, subnetQ := table["HOSTONLY_SUBNET"]
_, netmaskQ := table["HOSTONLY_NETMASK"]
if !(subnetQ && netmaskQ) {
log.Printf("Interface %s%d is missing some expected keys (HOSTONLY_SUBNET, HOSTONLY_NETMASK). This is non-critical. Ignoring..", NetworkingInterfacePrefix, vmnet)
}
// distinguish between nat or hostonly
if table["NAT"] == "yes" {
result[vmnet] = NetworkingType_NAT
} else {
result[vmnet] = NetworkingType_HOSTONLY
}
// if it's not a virtual_adapter, then it must be an alias (really a bridge).
} else {
result[vmnet] = NetworkingType_BRIDGED
}
}
return result
}
func networkingConfig_NamesToVmnet(config NetworkingConfig) map[NetworkingType][]int {
types := networkingConfig_InterfaceTypes(config)
// now sort the keys
var keys []int
for vmnet := range types {
keys = append(keys, vmnet)
}
sort.Ints(keys)
// build result dictionary
var result map[NetworkingType][]int
result = make(map[NetworkingType][]int)
for i := 0; i < len(keys); i++ {
t := types[keys[i]]
result[t] = append(result[t], keys[i])
}
return result
}
const NetworkingInterfacePrefix = "vmnet"
func (e NetworkingConfig) NameIntoDevices(name string) ([]string, error) {
netmapper := networkingConfig_NamesToVmnet(e)
name = strings.ToLower(name)
var vmnets []string
var networkingType NetworkingType
if name == "hostonly" && len(netmapper[NetworkingType_HOSTONLY]) > 0 {
networkingType = NetworkingType_HOSTONLY
} else if name == "nat" && len(netmapper[NetworkingType_NAT]) > 0 {
networkingType = NetworkingType_NAT
} else if name == "bridged" && len(netmapper[NetworkingType_BRIDGED]) > 0 {
networkingType = NetworkingType_BRIDGED
} else {
return make([]string, 0), fmt.Errorf("Network name not found: %v", name)
}
for i := 0; i < len(netmapper[networkingType]); i++ {
vmnets = append(vmnets, fmt.Sprintf("%s%d", NetworkingInterfacePrefix, netmapper[networkingType][i]))
}
return vmnets, nil
}
func (e NetworkingConfig) DeviceIntoName(device string) (string, error) {
types := networkingConfig_InterfaceTypes(e)
lowerdevice := strings.ToLower(device)
if !strings.HasPrefix(lowerdevice, NetworkingInterfacePrefix) {
return device, nil
}
vmnet, err := strconv.Atoi(lowerdevice[len(NetworkingInterfacePrefix):])
if err != nil {
return "", err
}
network := types[vmnet]
switch network {
case NetworkingType_HOSTONLY:
return "hostonly", nil
case NetworkingType_NAT:
return "nat", nil
case NetworkingType_BRIDGED:
return "bridged", nil
}
return "", fmt.Errorf("Unable to determine network type for device %s%d.", NetworkingInterfacePrefix, vmnet)
}
/** generic async file reader */
func consumeFile(fd *os.File) (chan byte, sentinelSignaller) {
fromfile := make(chan byte)
eof := make(sentinelSignaller)
go func() {
b := make([]byte, 1)
for {
_, err := fd.Read(b)
if err != nil {
// In case of any error we must stop
// ErrClosed may appear since file is closed and this goroutine still left running
break
}
fromfile <- b[0]
}
close(eof)
}()
return fromfile, eof
}