2015-11-11 07:39:06 -05:00
package common
2017-02-27 16:34:53 -05:00
2015-11-11 07:39:06 -05:00
import (
"fmt"
"io"
2017-10-23 21:26:16 -04:00
"log"
"math"
2015-11-11 07:39:06 -05:00
"net"
2017-02-27 16:34:53 -05:00
"os"
2017-10-23 21:26:16 -04:00
"reflect"
2015-11-11 07:39:06 -05:00
"sort"
2017-02-27 16:34:53 -05:00
"strconv"
"strings"
2015-11-11 07:39:06 -05:00
)
type sentinelSignaller chan struct { }
/** low-level parsing */
// strip the comments and extraneous newlines from a byte channel
2017-11-22 18:11:13 -05:00
func uncomment ( eof sentinelSignaller , in <- chan byte ) ( chan byte , sentinelSignaller ) {
2015-11-11 07:39:06 -05:00
out := make ( chan byte )
2017-11-22 18:11:13 -05:00
eoc := make ( sentinelSignaller )
2015-11-11 07:39:06 -05:00
go func ( in <- chan byte , out chan byte ) {
var endofline bool
for stillReading := true ; stillReading ; {
select {
2017-02-27 16:34:53 -05:00
case <- eof :
stillReading = false
case ch := <- in :
switch ch {
case '#' :
endofline = true
case '\n' :
if endofline {
endofline = false
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
}
if ! endofline {
out <- ch
}
2015-11-11 07:39:06 -05:00
}
}
2017-11-22 18:11:13 -05:00
close ( eoc )
2015-11-11 07:39:06 -05:00
} ( in , out )
2017-11-22 18:11:13 -05:00
return out , eoc
2015-11-11 07:39:06 -05:00
}
// convert a byte channel into a channel of pseudo-tokens
2017-11-22 18:11:13 -05:00
func tokenizeDhcpConfig ( eof sentinelSignaller , in chan byte ) ( chan string , sentinelSignaller ) {
2015-11-11 07:39:06 -05:00
var ch byte
var state string
var quote bool
2017-11-22 18:11:13 -05:00
eot := make ( sentinelSignaller )
2015-11-11 07:39:06 -05:00
out := make ( chan string )
go func ( out chan string ) {
for stillReading := true ; stillReading ; {
select {
2017-02-27 16:34:53 -05:00
case <- eof :
stillReading = false
case ch = <- in :
if quote {
if ch == '"' {
out <- state + string ( ch )
state , quote = "" , false
2015-11-11 07:39:06 -05:00
continue
}
2017-02-27 16:34:53 -05:00
state += string ( ch )
continue
}
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
out <- string ( ch )
state = ""
default :
state += string ( ch )
}
2015-11-11 07:39:06 -05:00
}
}
2017-02-27 16:34:53 -05:00
if len ( state ) > 0 {
out <- state
}
2017-11-22 18:11:13 -05:00
close ( eot )
2015-11-11 07:39:06 -05:00
} ( out )
2017-11-22 18:11:13 -05:00
return out , eot
2015-11-11 07:39:06 -05:00
}
/** mid-level parsing */
type tkParameter struct {
2017-02-27 16:34:53 -05:00
name string
2015-11-11 07:39:06 -05:00
operand [ ] string
}
2017-02-27 16:34:53 -05:00
2015-11-11 07:39:06 -05:00
func ( e * tkParameter ) String ( ) string {
var values [ ] string
2017-02-27 16:34:53 -05:00
for _ , val := range e . operand {
2015-11-11 07:39:06 -05:00
values = append ( values , val )
}
return fmt . Sprintf ( "%s [%s]" , e . name , strings . Join ( values , "," ) )
}
type tkGroup struct {
parent * tkGroup
2017-02-27 16:34:53 -05:00
id tkParameter
2015-11-11 07:39:06 -05:00
groups [ ] * tkGroup
params [ ] tkParameter
}
2017-02-27 16:34:53 -05:00
2015-11-11 07:39:06 -05:00
func ( e * tkGroup ) String ( ) string {
var id [ ] string
id = append ( id , e . id . name )
2017-02-27 16:34:53 -05:00
for _ , val := range e . id . operand {
2015-11-11 07:39:06 -05:00
id = append ( id , val )
}
var config [ ] string
2017-02-27 16:34:53 -05:00
for _ , val := range e . params {
2015-11-11 07:39:06 -05:00
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 {
2017-02-27 16:34:53 -05:00
case "{" :
fallthrough
case "}" :
fallthrough
case ";" :
goto leave
default :
result . operand = append ( result . operand , token )
2015-11-11 07:39:06 -05:00
}
}
leave :
return result
}
// convert a channel of pseudo-tokens into an tkGroup tree */
2017-02-27 16:34:53 -05:00
func parseDhcpConfig ( eof sentinelSignaller , in chan string ) ( tkGroup , error ) {
2015-11-11 07:39:06 -05:00
var tokens [ ] string
var result tkGroup
toParameter := func ( tokens [ ] string ) tkParameter {
out := make ( chan string )
2017-02-27 16:34:53 -05:00
go func ( out chan string ) {
for _ , v := range tokens {
out <- v
}
2015-11-11 07:39:06 -05:00
out <- ";"
} ( out )
return parseTokenParameter ( out )
}
2017-02-27 16:34:53 -05:00
for stillReading , currentgroup := true , & result ; stillReading ; {
2015-11-11 07:39:06 -05:00
select {
2017-02-27 16:34:53 -05:00
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 )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
currentgroup = currentgroup . parent
case ";" :
arg := toParameter ( tokens )
currentgroup . params = append ( currentgroup . params , arg )
default :
tokens = append ( tokens , tk )
continue
}
tokens = [ ] string { }
2015-11-11 07:39:06 -05:00
}
}
2017-02-27 16:34:53 -05:00
return result , nil
2015-11-11 07:39:06 -05:00
}
2017-11-22 18:11:13 -05:00
func tokenizeNetworkMapConfig ( eof sentinelSignaller , in chan byte ) ( chan string , sentinelSignaller ) {
2015-11-11 07:39:06 -05:00
var ch byte
var state string
var quote bool
var lastnewline bool
2017-11-22 18:11:13 -05:00
eot := make ( sentinelSignaller )
2015-11-11 07:39:06 -05:00
out := make ( chan string )
go func ( out chan string ) {
for stillReading := true ; stillReading ; {
select {
2017-02-27 16:34:53 -05:00
case <- eof :
stillReading = false
case ch = <- in :
if quote {
if ch == '"' {
out <- state + string ( ch )
state , quote = "" , false
2015-11-11 07:39:06 -05:00
continue
}
2017-02-27 16:34:53 -05:00
state += string ( ch )
continue
}
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
switch ch {
case '"' :
quote = true
state += string ( ch )
continue
case '\r' :
fallthrough
case '\t' :
fallthrough
case ' ' :
if len ( state ) == 0 {
continue
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
}
}
2017-02-27 16:34:53 -05:00
if len ( state ) > 0 {
out <- state
}
2017-11-22 18:11:13 -05:00
close ( eot )
2015-11-11 07:39:06 -05:00
} ( out )
2017-11-22 18:11:13 -05:00
return out , eot
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
func parseNetworkMapConfig ( eof sentinelSignaller , in chan string ) ( NetworkMap , error ) {
2015-11-11 07:39:06 -05:00
var unsorted map [ string ] map [ string ] string
var state [ ] string
addResult := func ( network string , attribute string , value string ) error {
2017-02-27 16:34:53 -05:00
_ , ok := unsorted [ network ]
if ! ok {
unsorted [ network ] = make ( map [ string ] string )
}
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
val , err := strconv . Unquote ( value )
if err != nil {
return err
}
2015-11-11 07:39:06 -05:00
current := unsorted [ network ]
current [ attribute ] = val
return nil
}
stillReading := true
for unsorted = make ( map [ string ] map [ string ] string ) ; stillReading ; {
select {
2017-02-27 16:34:53 -05:00
case <- eof :
if len ( state ) == 3 {
err := addResult ( state [ 0 ] , state [ 1 ] , state [ 2 ] )
if err != nil {
return nil , err
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
}
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 )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
err := addResult ( state [ 0 ] , state [ 1 ] , state [ 2 ] )
if err != nil {
return nil , err
}
state = make ( [ ] string , 0 )
default :
state = append ( state , tk )
}
2015-11-11 07:39:06 -05:00
}
}
result := make ( [ ] map [ string ] string , 0 )
var keys [ ] string
2017-02-27 16:34:53 -05:00
for k := range unsorted {
keys = append ( keys , k )
}
2015-11-11 07:39:06 -05:00
sort . Strings ( keys )
2017-02-27 16:34:53 -05:00
for _ , k := range keys {
2015-11-11 07:39:06 -05:00
result = append ( result , unsorted [ k ] )
}
2017-02-27 16:34:53 -05:00
return result , nil
2015-11-11 07:39:06 -05:00
}
/** higher-level parsing */
/// parameters
2017-02-27 16:34:53 -05:00
type pParameter interface {
repr ( ) string
}
2015-11-11 07:39:06 -05:00
type pParameterInclude struct {
filename string
}
2017-02-27 16:34:53 -05:00
func ( e pParameterInclude ) repr ( ) string { return fmt . Sprintf ( "include-file:filename=%s" , e . filename ) }
2015-11-11 07:39:06 -05:00
type pParameterOption struct {
2017-02-27 16:34:53 -05:00
name string
2015-11-11 07:39:06 -05:00
value string
}
2017-02-27 16:34:53 -05:00
func ( e pParameterOption ) repr ( ) string { return fmt . Sprintf ( "option:%s=%s" , e . name , e . value ) }
2015-11-11 07:39:06 -05:00
// allow some-kind-of-something
type pParameterGrant struct {
2017-02-27 16:34:53 -05:00
verb string // allow,deny,ignore
2015-11-11 07:39:06 -05:00
attribute string
}
2017-02-27 16:34:53 -05:00
func ( e pParameterGrant ) repr ( ) string { return fmt . Sprintf ( "grant:%s,%s" , e . verb , e . attribute ) }
2015-11-11 07:39:06 -05:00
type pParameterAddress4 [ ] string
2017-02-27 16:34:53 -05:00
2015-11-11 07:39:06 -05:00
func ( e pParameterAddress4 ) repr ( ) string {
2017-02-27 16:34:53 -05:00
return fmt . Sprintf ( "fixed-address4:%s" , strings . Join ( e , "," ) )
2015-11-11 07:39:06 -05:00
}
type pParameterAddress6 [ ] string
2017-02-27 16:34:53 -05:00
2015-11-11 07:39:06 -05:00
func ( e pParameterAddress6 ) repr ( ) string {
2017-02-27 16:34:53 -05:00
return fmt . Sprintf ( "fixed-address6:%s" , strings . Join ( e , "," ) )
2015-11-11 07:39:06 -05:00
}
// hardware address 00:00:00:00:00:00
type pParameterHardware struct {
2017-02-27 16:34:53 -05:00
class string
2015-11-11 07:39:06 -05:00
address [ ] byte
}
2017-02-27 16:34:53 -05:00
2015-11-11 07:39:06 -05:00
func ( e pParameterHardware ) repr ( ) string {
res := make ( [ ] string , 0 )
2017-02-27 16:34:53 -05:00
for _ , v := range e . address {
res = append ( res , fmt . Sprintf ( "%02x" , v ) )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
return fmt . Sprintf ( "hardware-address:%s[%s]" , e . class , strings . Join ( res , ":" ) )
2015-11-11 07:39:06 -05:00
}
type pParameterBoolean struct {
parameter string
2017-02-27 16:34:53 -05:00
truancy bool
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
func ( e pParameterBoolean ) repr ( ) string { return fmt . Sprintf ( "boolean:%s=%v" , e . parameter , e . truancy ) }
2015-11-11 07:39:06 -05:00
type pParameterClientMatch struct {
name string
data string
}
2017-02-27 16:34:53 -05:00
func ( e pParameterClientMatch ) repr ( ) string { return fmt . Sprintf ( "match-client:%s=%s" , e . name , e . data ) }
2015-11-11 07:39:06 -05:00
// range 127.0.0.1 127.0.0.255
type pParameterRange4 struct {
min net . IP
max net . IP
}
2017-02-27 16:34:53 -05:00
func ( e pParameterRange4 ) repr ( ) string {
return fmt . Sprintf ( "range4:%s-%s" , e . min . String ( ) , e . max . String ( ) )
}
2015-11-11 07:39:06 -05:00
type pParameterRange6 struct {
min net . IP
max net . IP
}
2017-02-27 16:34:53 -05:00
func ( e pParameterRange6 ) repr ( ) string {
return fmt . Sprintf ( "range6:%s-%s" , e . min . String ( ) , e . max . String ( ) )
}
2015-11-11 07:39:06 -05:00
type pParameterPrefix6 struct {
2017-02-27 16:34:53 -05:00
min net . IP
max net . IP
2015-11-11 07:39:06 -05:00
bits int
}
2017-02-27 16:34:53 -05:00
func ( e pParameterPrefix6 ) repr ( ) string {
return fmt . Sprintf ( "prefix6:/%d:%s-%s" , e . bits , e . min . String ( ) , e . max . String ( ) )
}
2015-11-11 07:39:06 -05:00
// some-kind-of-parameter 1024
type pParameterOther struct {
parameter string
2017-02-27 16:34:53 -05:00
value string
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
func ( e pParameterOther ) repr ( ) string { return fmt . Sprintf ( "parameter:%s=%s" , e . parameter , e . value ) }
2015-11-11 07:39:06 -05:00
type pParameterExpression struct {
2017-02-27 16:34:53 -05:00
parameter string
2015-11-11 07:39:06 -05:00
expression string
}
2017-02-27 16:34:53 -05:00
func ( e pParameterExpression ) repr ( ) string {
return fmt . Sprintf ( "parameter-expression:%s=\"%s\"" , e . parameter , e . expression )
}
type pDeclarationIdentifier interface {
repr ( ) string
}
2015-11-11 07:39:06 -05:00
type pDeclaration struct {
2017-02-27 16:34:53 -05:00
id pDeclarationIdentifier
parent * pDeclaration
parameters [ ] pParameter
2015-11-11 07:39:06 -05:00
declarations [ ] pDeclaration
}
func ( e * pDeclaration ) short ( ) string {
return e . id . repr ( )
}
func ( e * pDeclaration ) repr ( ) string {
res := e . short ( )
var parameters [ ] string
2017-02-27 16:34:53 -05:00
for _ , v := range e . parameters {
2015-11-11 07:39:06 -05:00
parameters = append ( parameters , v . repr ( ) )
}
var groups [ ] string
2017-02-27 16:34:53 -05:00
for _ , v := range e . declarations {
groups = append ( groups , fmt . Sprintf ( "-> %s" , v . short ( ) ) )
2015-11-11 07:39:06 -05:00
}
if e . parent != nil {
2017-02-27 16:34:53 -05:00
res = fmt . Sprintf ( "%s parent:%s" , res , e . parent . short ( ) )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
return fmt . Sprintf ( "%s\n%s\n%s\n" , res , strings . Join ( parameters , "\n" ) , strings . Join ( groups , "\n" ) )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
type pDeclarationGlobal struct { }
2015-11-11 07:39:06 -05:00
func ( e pDeclarationGlobal ) repr ( ) string { return fmt . Sprintf ( "{global}" ) }
2017-02-27 16:34:53 -05:00
type pDeclarationShared struct { name string }
2015-11-11 07:39:06 -05:00
func ( e pDeclarationShared ) repr ( ) string { return fmt . Sprintf ( "{shared-network %s}" , e . name ) }
2017-02-27 16:34:53 -05:00
type pDeclarationSubnet4 struct { net . IPNet }
2015-11-11 07:39:06 -05:00
func ( e pDeclarationSubnet4 ) repr ( ) string { return fmt . Sprintf ( "{subnet4 %s}" , e . String ( ) ) }
2017-02-27 16:34:53 -05:00
type pDeclarationSubnet6 struct { net . IPNet }
2015-11-11 07:39:06 -05:00
func ( e pDeclarationSubnet6 ) repr ( ) string { return fmt . Sprintf ( "{subnet6 %s}" , e . String ( ) ) }
2017-02-27 16:34:53 -05:00
type pDeclarationHost struct { name string }
2015-11-11 07:39:06 -05:00
func ( e pDeclarationHost ) repr ( ) string { return fmt . Sprintf ( "{host name:%s}" , e . name ) }
2017-02-27 16:34:53 -05:00
type pDeclarationPool struct { }
2015-11-11 07:39:06 -05:00
func ( e pDeclarationPool ) repr ( ) string { return fmt . Sprintf ( "{pool}" ) }
2017-02-27 16:34:53 -05:00
type pDeclarationGroup struct { }
2015-11-11 07:39:06 -05:00
func ( e pDeclarationGroup ) repr ( ) string { return fmt . Sprintf ( "{group}" ) }
2017-02-27 16:34:53 -05:00
type pDeclarationClass struct { name string }
2015-11-11 07:39:06 -05:00
func ( e pDeclarationClass ) repr ( ) string { return fmt . Sprintf ( "{class}" ) }
/** parsers */
2017-02-27 16:34:53 -05:00
func parseParameter ( val tkParameter ) ( pParameter , error ) {
2015-11-11 07:39:06 -05:00
switch val . name {
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
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 )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
address := net . ParseIP ( cidr [ 0 ] )
bits , err := strconv . Atoi ( cidr [ 1 ] )
if err != nil {
return nil , err
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
mask := net . CIDRMask ( bits , net . IPv6len * 8 )
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
// figure out the network address
network := address . Mask ( mask )
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
// 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
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
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 )
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
address = append ( address , byte ( b ) )
}
return pParameterHardware { class : class , address : address } , nil
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
}
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
2015-11-11 07:39:06 -05:00
}
}
2017-02-27 16:34:53 -05:00
func parseTokenGroup ( val tkGroup ) ( * pDeclaration , error ) {
2015-11-11 07:39:06 -05:00
params := val . id . operand
switch val . id . name {
2017-02-27 16:34:53 -05:00
case "group" :
return & pDeclaration { id : pDeclarationGroup { } } , nil
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
case "pool" :
return & pDeclaration { id : pDeclarationPool { } } , nil
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
case "host" :
if len ( params ) == 1 {
return & pDeclaration { id : pDeclarationHost { name : params [ 0 ] } } , nil
}
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
addr [ i ] = byte ( res )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
}
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
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
}
case "shared-network" :
if len ( params ) == 1 {
return & pDeclaration { id : pDeclarationShared { name : params [ 0 ] } } , nil
}
case "" :
return & pDeclaration { id : pDeclarationGlobal { } } , nil
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
return nil , fmt . Errorf ( "Invalid pDeclaration : %v : %v" , val . id . name , params )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
func flattenDhcpConfig ( root tkGroup ) ( * pDeclaration , error ) {
2015-11-11 07:39:06 -05:00
var result * pDeclaration
2017-02-27 16:34:53 -05:00
result , err := parseTokenGroup ( root )
if err != nil {
return nil , err
}
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
for _ , p := range root . params {
param , err := parseParameter ( p )
if err != nil {
return nil , err
}
2015-11-11 07:39:06 -05:00
result . parameters = append ( result . parameters , param )
}
2017-02-27 16:34:53 -05:00
for _ , p := range root . groups {
group , err := flattenDhcpConfig ( * p )
if err != nil {
return nil , err
}
2015-11-11 07:39:06 -05:00
group . parent = result
result . declarations = append ( result . declarations , * group )
}
2017-02-27 16:34:53 -05:00
return result , nil
2015-11-11 07:39:06 -05:00
}
/** reduce the tree into the things that we care about */
type grant uint
2017-02-27 16:34:53 -05:00
2015-11-11 07:39:06 -05:00
const (
2017-02-27 16:34:53 -05:00
ALLOW grant = iota
2015-11-11 07:39:06 -05:00
IGNORE grant = iota
2017-02-27 16:34:53 -05:00
DENY grant = iota
2015-11-11 07:39:06 -05:00
)
2017-02-27 16:34:53 -05:00
2015-11-11 07:39:06 -05:00
type configDeclaration struct {
2017-02-27 16:34:53 -05:00
id [ ] pDeclarationIdentifier
2015-11-11 07:39:06 -05:00
composites [ ] pDeclaration
address [ ] pParameter
2017-02-27 16:34:53 -05:00
options map [ string ] string
grants map [ string ] grant
attributes map [ string ] bool
parameters map [ string ] string
2015-11-11 07:39:06 -05:00
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
2017-02-27 16:34:53 -05:00
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 )
2015-11-11 07:39:06 -05:00
// update configDeclaration parameters
2017-02-27 16:34:53 -05:00
for _ , p := range hierarchy [ i ] . parameters {
2015-11-11 07:39:06 -05:00
switch p . ( type ) {
2017-02-27 16:34:53 -05:00
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 )
2015-11-11 07:39:06 -05:00
}
}
}
return result
}
func ( e * configDeclaration ) repr ( ) string {
var result [ ] string
var res [ ] string
res = make ( [ ] string , 0 )
2017-02-27 16:34:53 -05:00
for _ , v := range e . id {
res = append ( res , v . repr ( ) )
}
2015-11-11 07:39:06 -05:00
result = append ( result , strings . Join ( res , "," ) )
if len ( e . address ) > 0 {
res = make ( [ ] string , 0 )
2017-02-27 16:34:53 -05:00
for _ , v := range e . address {
res = append ( res , v . repr ( ) )
}
2015-11-11 07:39:06 -05:00
result = append ( result , fmt . Sprintf ( "address : %v" , strings . Join ( res , "," ) ) )
}
2017-02-27 16:34:53 -05:00
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 ) )
}
2015-11-11 07:39:06 -05:00
if len ( e . hostid ) > 0 {
res = make ( [ ] string , 0 )
2017-02-27 16:34:53 -05:00
for _ , v := range e . hostid {
res = append ( res , v . repr ( ) )
}
2015-11-11 07:39:06 -05:00
result = append ( result , fmt . Sprintf ( "hostid : %v" , strings . Join ( res , " " ) ) )
}
return strings . Join ( result , "\n" ) + "\n"
}
2017-02-27 16:34:53 -05:00
func ( e * configDeclaration ) IP4 ( ) ( net . IP , error ) {
2015-11-11 07:39:06 -05:00
var result [ ] string
2017-02-27 16:34:53 -05:00
for _ , entry := range e . address {
2015-11-11 07:39:06 -05:00
switch entry . ( type ) {
2017-02-27 16:34:53 -05:00
case pParameterAddress4 :
for _ , s := range entry . ( pParameterAddress4 ) {
result = append ( result , s )
}
2015-11-11 07:39:06 -05:00
}
}
if len ( result ) > 1 {
2017-02-27 16:34:53 -05:00
return nil , fmt . Errorf ( "More than one address4 returned : %v" , result )
2015-11-11 07:39:06 -05:00
} else if len ( result ) == 0 {
2017-02-27 16:34:53 -05:00
return nil , fmt . Errorf ( "No IP4 addresses found" )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
func ( e * configDeclaration ) IP6 ( ) ( net . IP , error ) {
2015-11-11 07:39:06 -05:00
var result [ ] string
2017-02-27 16:34:53 -05:00
for _ , entry := range e . address {
2015-11-11 07:39:06 -05:00
switch entry . ( type ) {
2017-02-27 16:34:53 -05:00
case pParameterAddress6 :
for _ , s := range entry . ( pParameterAddress6 ) {
result = append ( result , s )
}
2015-11-11 07:39:06 -05:00
}
}
if len ( result ) > 1 {
2017-02-27 16:34:53 -05:00
return nil , fmt . Errorf ( "More than one address6 returned : %v" , result )
2015-11-11 07:39:06 -05:00
} else if len ( result ) == 0 {
2017-02-27 16:34:53 -05:00
return nil , fmt . Errorf ( "No IP6 addresses found" )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
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
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
func ( e * configDeclaration ) Hardware ( ) ( net . HardwareAddr , error ) {
2015-11-11 07:39:06 -05:00
var result [ ] pParameterHardware
2017-02-27 16:34:53 -05:00
for _ , addr := range e . address {
2015-11-11 07:39:06 -05:00
switch addr . ( type ) {
2017-02-27 16:34:53 -05:00
case pParameterHardware :
result = append ( result , addr . ( pParameterHardware ) )
2015-11-11 07:39:06 -05:00
}
}
if len ( result ) > 0 {
2017-02-27 16:34:53 -05:00
return nil , fmt . Errorf ( "More than one hardware address returned : %v" , result )
2015-11-11 07:39:06 -05:00
}
res := make ( net . HardwareAddr , 0 )
2017-02-27 16:34:53 -05:00
for _ , by := range result [ 0 ] . address {
2015-11-11 07:39:06 -05:00
res = append ( res , by )
}
2017-02-27 16:34:53 -05:00
return res , nil
2015-11-11 07:39:06 -05:00
}
/*** Dhcp Configuration */
type DhcpConfiguration [ ] configDeclaration
2017-02-27 16:34:53 -05:00
func ReadDhcpConfiguration ( fd * os . File ) ( DhcpConfiguration , error ) {
fromfile , eof := consumeFile ( fd )
2017-11-22 18:11:13 -05:00
uncommented , eoc := uncomment ( eof , fromfile )
tokenized , eot := tokenizeDhcpConfig ( eoc , uncommented )
parsetree , err := parseDhcpConfig ( eot , tokenized )
2017-02-27 16:34:53 -05:00
if err != nil {
return nil , err
}
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
global , err := flattenDhcpConfig ( parsetree )
if err != nil {
return nil , err
}
2015-11-11 07:39:06 -05:00
2017-02-27 16:34:53 -05:00
var walkDeclarations func ( root pDeclaration , out chan * configDeclaration )
walkDeclarations = func ( root pDeclaration , out chan * configDeclaration ) {
2015-11-11 07:39:06 -05:00
res := createDeclaration ( root )
out <- & res
2017-02-27 16:34:53 -05:00
for _ , p := range root . declarations {
2015-11-11 07:39:06 -05:00
walkDeclarations ( p , out )
}
}
2017-02-27 16:34:53 -05:00
each := make ( chan * configDeclaration )
go func ( out chan * configDeclaration ) {
2015-11-11 07:39:06 -05:00
walkDeclarations ( * global , out )
out <- nil
} ( each )
var result DhcpConfiguration
for decl := <- each ; decl != nil ; decl = <- each {
result = append ( result , * decl )
}
2017-02-27 16:34:53 -05:00
return result , nil
2015-11-11 07:39:06 -05:00
}
func ( e * DhcpConfiguration ) Global ( ) configDeclaration {
result := ( * e ) [ 0 ]
if len ( result . id ) != 1 {
panic ( fmt . Errorf ( "Something that can't happen happened" ) )
}
return result
}
2017-02-27 16:34:53 -05:00
func ( e * DhcpConfiguration ) SubnetByAddress ( address net . IP ) ( configDeclaration , error ) {
2015-11-11 07:39:06 -05:00
var result [ ] configDeclaration
2017-02-27 16:34:53 -05:00
for _ , entry := range * e {
2015-11-11 07:39:06 -05:00
switch entry . id [ 0 ] . ( type ) {
2017-02-27 16:34:53 -05:00
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 )
}
2015-11-11 07:39:06 -05:00
}
}
if len ( result ) == 0 {
2017-02-27 16:34:53 -05:00
return configDeclaration { } , fmt . Errorf ( "No network declarations containing %s found" , address . String ( ) )
2015-11-11 07:39:06 -05:00
}
if len ( result ) > 1 {
2017-02-27 16:34:53 -05:00
return configDeclaration { } , fmt . Errorf ( "More than 1 network declaration found : %v" , result )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
return result [ 0 ] , nil
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
func ( e * DhcpConfiguration ) HostByName ( host string ) ( configDeclaration , error ) {
2015-11-11 07:39:06 -05:00
var result [ ] configDeclaration
2017-02-27 16:34:53 -05:00
for _ , entry := range * e {
2015-11-11 07:39:06 -05:00
switch entry . id [ 0 ] . ( type ) {
2017-02-27 16:34:53 -05:00
case pDeclarationHost :
id := entry . id [ 0 ] . ( pDeclarationHost )
if strings . ToLower ( id . name ) == strings . ToLower ( host ) {
result = append ( result , entry )
}
2015-11-11 07:39:06 -05:00
}
}
if len ( result ) == 0 {
2017-02-27 16:34:53 -05:00
return configDeclaration { } , fmt . Errorf ( "No host declarations containing %s found" , host )
2015-11-11 07:39:06 -05:00
}
if len ( result ) > 1 {
2017-02-27 16:34:53 -05:00
return configDeclaration { } , fmt . Errorf ( "More than 1 host declaration found : %v" , result )
2015-11-11 07:39:06 -05:00
}
2017-02-27 16:34:53 -05:00
return result [ 0 ] , nil
2015-11-11 07:39:06 -05:00
}
/*** Network Map */
type NetworkMap [ ] map [ string ] string
2017-10-23 21:26:16 -04:00
type NetworkNameMapper interface {
NameIntoDevice ( string ) ( string , error )
DeviceIntoName ( string ) ( string , error )
}
2017-02-27 16:34:53 -05:00
func ReadNetworkMap ( fd * os . File ) ( NetworkMap , error ) {
fromfile , eof := consumeFile ( fd )
2017-11-22 18:11:13 -05:00
uncommented , eoc := uncomment ( eof , fromfile )
tokenized , eot := tokenizeNetworkMapConfig ( eoc , uncommented )
2015-11-11 07:39:06 -05:00
2017-11-22 18:11:13 -05:00
result , err := parseNetworkMapConfig ( eot , tokenized )
2017-02-27 16:34:53 -05:00
if err != nil {
return nil , err
}
return result , nil
2015-11-11 07:39:06 -05:00
}
2017-10-23 21:26:16 -04:00
func ( e NetworkMap ) NameIntoDevice ( name string ) ( string , error ) {
for _ , val := range e {
2015-11-11 07:39:06 -05:00
if strings . ToLower ( val [ "name" ] ) == strings . ToLower ( name ) {
2017-02-27 16:34:53 -05:00
return val [ "device" ] , nil
2015-11-11 07:39:06 -05:00
}
}
2017-02-27 16:34:53 -05:00
return "" , fmt . Errorf ( "Network name not found : %v" , name )
2015-11-11 07:39:06 -05:00
}
2017-10-23 21:26:16 -04:00
func ( e NetworkMap ) DeviceIntoName ( device string ) ( string , error ) {
for _ , val := range e {
2015-11-11 07:39:06 -05:00
if strings . ToLower ( val [ "device" ] ) == strings . ToLower ( device ) {
2017-02-27 16:34:53 -05:00
return val [ "name" ] , nil
2015-11-11 07:39:06 -05:00
}
}
2017-02-27 16:34:53 -05:00
return "" , fmt . Errorf ( "Device name not found : %v" , device )
2015-11-11 07:39:06 -05:00
}
func ( e * NetworkMap ) repr ( ) string {
var result [ ] string
2017-02-27 16:34:53 -05:00
for idx , val := range * e {
2015-11-11 07:39:06 -05:00
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" )
}
2017-10-23 21:26:16 -04:00
/*** parser for VMware Fusion's networking file */
2017-11-22 18:11:13 -05:00
func tokenizeNetworkingConfig ( eof sentinelSignaller , in chan byte ) ( chan string , sentinelSignaller ) {
2017-10-23 21:26:16 -04:00
var ch byte
var state string
var repeat_newline bool
2017-11-22 18:11:13 -05:00
eot := make ( sentinelSignaller )
2017-10-23 21:26:16 -04:00
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
}
2017-11-22 18:11:13 -05:00
close ( eot )
2017-10-23 21:26:16 -04:00
} ( out )
2017-11-22 18:11:13 -05:00
return out , eot
2017-10-23 21:26:16 -04:00
}
2017-11-22 18:11:13 -05:00
func splitNetworkingConfig ( eof sentinelSignaller , in chan string ) ( chan [ ] string , sentinelSignaller ) {
2017-10-23 21:26:16 -04:00
var out chan [ ] string
2017-11-22 18:11:13 -05:00
eos := make ( sentinelSignaller )
2017-10-23 21:26:16 -04:00
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
}
2017-11-22 18:11:13 -05:00
close ( eos )
2017-10-23 21:26:16 -04:00
} ( out )
2017-11-22 18:11:13 -05:00
return out , eos
2017-10-23 21:26:16 -04:00
}
/// 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
}
2017-11-22 18:11:13 -05:00
func parseNetworkingConfig ( eof sentinelSignaller , rows chan [ ] string ) ( chan networkingCommandEntry , sentinelSignaller ) {
2017-10-23 21:26:16 -04:00
var out chan networkingCommandEntry
2017-11-22 18:11:13 -05:00
eop := make ( sentinelSignaller )
2017-10-23 21:26:16 -04:00
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
}
}
}
2017-11-22 18:11:13 -05:00
close ( eop )
2017-10-23 21:26:16 -04:00
} ( rows , out )
2017-11-22 18:11:13 -05:00
return out , eop
2017-10-23 21:26:16 -04:00
}
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 )
2017-11-22 18:11:13 -05:00
tokenized , eot := tokenizeNetworkingConfig ( eof , fromfile )
rows , eos := splitNetworkingConfig ( eot , tokenized )
entries , eop := parseNetworkingConfig ( eos , rows )
2017-10-23 21:26:16 -04:00
// 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
2017-11-22 18:11:13 -05:00
result := flattenNetworkingConfig ( eop , entries )
2017-10-23 21:26:16 -04:00
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 ) NameIntoDevice ( name string ) ( string , error ) {
netmapper := networkingConfig_NamesToVmnet ( e )
name = strings . ToLower ( name )
var vmnet int
if name == "hostonly" && len ( netmapper [ NetworkingType_HOSTONLY ] ) > 0 {
vmnet = netmapper [ NetworkingType_HOSTONLY ] [ 0 ]
} else if name == "nat" && len ( netmapper [ NetworkingType_NAT ] ) > 0 {
vmnet = netmapper [ NetworkingType_NAT ] [ 0 ]
} else if name == "bridged" && len ( netmapper [ NetworkingType_BRIDGED ] ) > 0 {
vmnet = netmapper [ NetworkingType_BRIDGED ] [ 0 ]
} else {
return "" , fmt . Errorf ( "Network name not found : %v" , name )
}
return fmt . Sprintf ( "%s%d" , NetworkingInterfacePrefix , vmnet ) , 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 */
2017-02-27 16:34:53 -05:00
func consumeFile ( fd * os . File ) ( chan byte , sentinelSignaller ) {
2015-11-11 07:39:06 -05:00
fromfile := make ( chan byte )
eof := make ( sentinelSignaller )
go func ( ) {
b := make ( [ ] byte , 1 )
for {
2017-02-27 16:34:53 -05:00
_ , err := fd . Read ( b )
if err == io . EOF {
break
}
2015-11-11 07:39:06 -05:00
fromfile <- b [ 0 ]
}
close ( eof )
} ( )
2017-02-27 16:34:53 -05:00
return fromfile , eof
2015-11-11 07:39:06 -05:00
}