"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "go/pkg/pass1/setup-objects.go" between
Netspoc-6.026.tar.gz and Netspoc-6.027.tar.gz

About: NetSPoC is a network security policy compiler (using its own description language) to manage all the packet filter devices inside your network topology.

setup-objects.go  (Netspoc-6.026):setup-objects.go  (Netspoc-6.027)
package pass1 package pass1
import ( import (
"bytes"
"fmt" "fmt"
"github.com/hknutzen/Netspoc/go/pkg/ast" "github.com/hknutzen/Netspoc/go/pkg/ast"
"github.com/hknutzen/Netspoc/go/pkg/filetree" "github.com/hknutzen/Netspoc/go/pkg/filetree"
"github.com/hknutzen/Netspoc/go/pkg/jcode" "github.com/hknutzen/Netspoc/go/pkg/jcode"
"github.com/hknutzen/Netspoc/go/pkg/parser" "github.com/hknutzen/Netspoc/go/pkg/parser"
"net" "inet.af/netaddr"
"regexp" "regexp"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"time" "time"
) )
var symTable *symbolTable var symTable *symbolTable
func (c *spoc) readNetspoc(path string) { func (c *spoc) readNetspoc(path string) {
skipping to change at line 65 skipping to change at line 64
func (c *spoc) setupTopology(toplevel []ast.Toplevel) { func (c *spoc) setupTopology(toplevel []ast.Toplevel) {
c.checkDuplicate(toplevel) c.checkDuplicate(toplevel)
sym := createSymbolTable() sym := createSymbolTable()
c.initStdProtocols(sym) c.initStdProtocols(sym)
symTable = sym symTable = sym
c.setupObjects(toplevel, sym) c.setupObjects(toplevel, sym)
c.stopOnErr() c.stopOnErr()
c.linkTunnels(sym) c.linkTunnels(sym)
c.linkVirtualInterfaces() c.linkVirtualInterfaces()
c.splitSemiManagedRouter()
} }
type symbolTable struct { type symbolTable struct {
// Leaf nodes, referencing nothing. // Leaf nodes, referencing nothing.
isakmp map[string]*isakmp isakmp map[string]*isakmp
owner map[string]*owner owner map[string]*owner
// Named protocols // Named protocols
protocol map[string]*proto protocol map[string]*proto
// Unnamed protocols like "tcp 80" // Unnamed protocols like "tcp 80"
unnamedProto map[string]*proto unnamedProto map[string]*proto
skipping to change at line 674 skipping to change at line 672
n.ipType = bridgedIP n.ipType = bridgedIP
} }
if i != -1 && !isSimpleName(netName[:i]) || !isSimpleName(netName[i+1:]) { if i != -1 && !isSimpleName(netName[:i]) || !isSimpleName(netName[i+1:]) {
c.err("Invalid identifier in definition of '%s'", name) c.err("Invalid identifier in definition of '%s'", name)
} }
var ldapAppend string var ldapAppend string
hasIP := false hasIP := false
for _, a := range v.Attributes { for _, a := range v.Attributes {
switch a.Name { switch a.Name {
case "ip": case "ip":
n.ip, n.mask = c.getIpPrefix(a, v.IPV6, name) n.ipp = c.getIpPrefix(a, v.IPV6, name)
hasIP = true hasIP = true
case "unnumbered": case "unnumbered":
if c.getFlag(a, name) { if c.getFlag(a, name) {
if n.ipType == bridgedIP { if n.ipType == bridgedIP {
c.err("Unnumbered %s must not be bridged" , name) c.err("Unnumbered %s must not be bridged" , name)
} }
n.ipType = unnumberedIP n.ipType = unnumberedIP
} }
case "has_subnets": case "has_subnets":
n.hasSubnets = c.getFlag(a, name) n.hasSubnets = c.getFlag(a, name)
skipping to change at line 735 skipping to change at line 733
c.err("Unnumbered %s must not have attrib ute '%s'", c.err("Unnumbered %s must not have attrib ute '%s'",
name, a.Name) name, a.Name)
} }
} }
} }
if len(n.hosts) != 0 { if len(n.hosts) != 0 {
c.err("Unnumbered %s must not have host definition", name ) c.err("Unnumbered %s must not have host definition", name )
} }
} else if n.ipType == bridgedIP { } else if n.ipType == bridgedIP {
for _, h := range n.hosts { for _, h := range n.hosts {
if h.ipRange[0] != nil { if !h.ipRange.From.IsZero() {
c.err("Bridged %s must not have %s with range (no t implemented)", c.err("Bridged %s must not have %s with range (no t implemented)",
name, h.name) name, h.name)
} }
} }
for _, nat := range n.nat { for _, nat := range n.nat {
if !nat.identity { if !nat.identity {
c.err("Only identity NAT allowed for bridged %s", n.name) c.err("Only identity NAT allowed for bridged %s", n)
break break
} }
} }
} else if n.ip == nil && !hasIP { } else if n.ipp.IsZero() && !hasIP {
c.err("Missing IP address for %s", name) c.err("Missing IP address for %s", name)
} else { } else {
ip := n.ip ipp := n.ipp
mask := n.mask
for _, h := range n.hosts { for _, h := range n.hosts {
// Check compatibility of host IP and network IP/mask. // Check compatibility of host IP and network IP/mask.
if h.ip != nil { if !h.ip.IsZero() {
if !matchIp(h.ip, ip, mask) { if !ipp.Contains(h.ip) {
c.err("IP of %s doesn't match IP/mask of c.err("IP of %s doesn't match IP/mask of
%s", h.name, name) %s", h, name)
} }
} else { } else {
// Check range. // Check range.
if !(matchIp(h.ipRange[0], ip, mask) && if !(ipp.Contains(h.ipRange.From) && ipp.Contains
matchIp(h.ipRange[1], ip, mask)) { (h.ipRange.To)) {
c.err("IP range of %s doesn't match IP/ma c.err("IP range of %s doesn't match IP/ma
sk of %s", sk of %s", h, name)
h.name, name)
} }
} }
// Compatibility of host and network NAT will be checked later, // Compatibility of host and network NAT will be checked later,
// after inherited NAT definitions have been processed. // after inherited NAT definitions have been processed.
} }
if n.hosts != nil && n.crosslink { if n.hosts != nil && n.crosslink {
c.err("Crosslink %s must not have host definitions", name ) c.err("Crosslink %s must not have host definitions", name )
} }
// Check NAT definitions. // Check NAT definitions.
for tag, nat := range n.nat { for tag, nat := range n.nat {
if !nat.dynamic { if !nat.dynamic {
if bytes.Compare(nat.mask, mask) != 0 { if nat.ipp.Bits != ipp.Bits {
c.err("Mask for non dynamic nat:%s must b e equal to mask of %s", c.err("Mask for non dynamic nat:%s must b e equal to mask of %s",
tag, name) tag, name)
} }
} }
} }
// Check and mark networks with ID-hosts. // Check and mark networks with ID-hosts.
ldapCount := 0 ldapCount := 0
idHostsCount := 0 idHostsCount := 0
for _, h := range n.hosts { for _, h := range n.hosts {
skipping to change at line 882 skipping to change at line 877
case "radius_attributes": case "radius_attributes":
h.radiusAttributes = c.getRadiusAttributes(a, name) h.radiusAttributes = c.getRadiusAttributes(a, name)
default: default:
if nat := c.addIPNat(a, h.nat, v6, name); nat != nil { if nat := c.addIPNat(a, h.nat, v6, name); nat != nil {
h.nat = nat h.nat = nat
} else { } else {
c.err("Unexpected attribute in %s: %s", name, a.N ame) c.err("Unexpected attribute in %s: %s", name, a.N ame)
} }
} }
} }
if (h.ip == nil) == (h.ipRange[0] == nil) { if h.ip.IsZero() == h.ipRange.From.IsZero() {
c.err("%s needs exactly one of attributes 'ip' and 'range'", name ) c.err("%s needs exactly one of attributes 'ip' and 'range'", name )
} }
if h.id != "" { if h.id != "" {
if h.ldapId != "" { if h.ldapId != "" {
c.warn("Ignoring attribute 'ldap_id' at %s", name) c.warn("Ignoring attribute 'ldap_id' at %s", name)
h.ldapId = "" h.ldapId = ""
} }
} else if h.ldapId != "" { } else if h.ldapId != "" {
if h.ipRange[0] == nil { if h.ipRange.From.IsZero() {
c.err("Attribute 'ldap_Id' must only be used together wit h"+ c.err("Attribute 'ldap_Id' must only be used together wit h"+
" IP range at %s", name) " IP range at %s", name)
} }
} else if h.radiusAttributes != nil { } else if h.radiusAttributes != nil {
c.warn("Ignoring 'radius_attributes' at %s", name) c.warn("Ignoring 'radius_attributes' at %s", name)
} }
if h.nat != nil && h.ipRange[0] != nil { if h.nat != nil && !h.ipRange.From.IsZero() {
// Before changing this, // Before changing this,
// add consistency tests in convert_hosts. // add consistency tests in convert_hosts.
c.err("No NAT supported for %s with 'range'", name) c.err("No NAT supported for %s with 'range'", name)
} }
return h return h
} }
func (c *spoc) setupAggregate(v *ast.TopStruct, s *symbolTable) { func (c *spoc) setupAggregate(v *ast.TopStruct, s *symbolTable) {
name := v.Name name := v.Name
v6 := v.IPV6 v6 := v.IPV6
ag := new(network) ag := new(network)
ag.name = name ag.name = name
ag.isAggregate = true ag.isAggregate = true
ag.ipV6 = v6 ag.ipV6 = v6
agName := name[len("any:"):] agName := name[len("any:"):]
s.aggregate[agName] = ag s.aggregate[agName] = ag
hasLink := false hasLink := false
for _, a := range v.Attributes { for _, a := range v.Attributes {
switch a.Name { switch a.Name {
case "ip": case "ip":
ag.ip, ag.mask = c.getIpPrefix(a, v.IPV6, name) ag.ipp = c.getIpPrefix(a, v.IPV6, name)
case "link": case "link":
hasLink = true hasLink = true
ag.link = c.getNetworkRef(a, s, v6, name) ag.link = c.getNetworkRef(a, s, v6, name)
case "no_check_supernet_rules": case "no_check_supernet_rules":
ag.noCheckSupernetRules = c.getFlag(a, name) ag.noCheckSupernetRules = c.getFlag(a, name)
case "owner": case "owner":
ag.owner = c.getRealOwnerRef(a, s, name) ag.owner = c.getRealOwnerRef(a, s, name)
default: default:
if c.addAttr(a, &ag.attr, name) { if c.addAttr(a, &ag.attr, name) {
} else if nat := c.addNetNat(a, ag.nat, v.IPV6, s, name); nat != nil { } else if nat := c.addNetNat(a, ag.nat, v.IPV6, s, name); nat != nil {
ag.nat = nat ag.nat = nat
} else { } else {
c.err("Unexpected attribute in %s: %s", name, a.N ame) c.err("Unexpected attribute in %s: %s", name, a.N ame)
} }
} }
} }
c.checkDuplAttr(v.Attributes, name) c.checkDuplAttr(v.Attributes, name)
if !hasLink { if !hasLink {
c.err("Attribute 'link' must be defined for %s", name) c.err("Attribute 'link' must be defined for %s", name)
} }
if ag.link == nil { if ag.ipp.IsZero() {
ag.disabled = true ag.ipp = getNetwork00(v6).ipp
} }
if len(ag.ip) == 0 { if ag.ipp.Bits != 0 {
ag.ip = getZeroIp(v6)
ag.mask = getZeroMask(v6)
}
if size, _ := ag.mask.Size(); size != 0 {
for _, a := range v.Attributes { for _, a := range v.Attributes {
switch a.Name { switch a.Name {
case "ip", "link", "owner": case "ip", "link", "owner":
continue continue
} }
if !strings.HasPrefix(a.Name, "nat:") { if !strings.HasPrefix(a.Name, "nat:") {
c.err("Must not use attribute '%s' if IP is set f or %s", c.err("Must not use attribute '%s' if IP is set f or %s",
a.Name, name) a.Name, name)
} }
} }
skipping to change at line 1004 skipping to change at line 995
return nil return nil
} }
ctx := "'" + att + "' of " + name ctx := "'" + att + "' of " + name
l := c.expandGroup(u.Elements, ctx, v.IPV6, false) l := c.expandGroup(u.Elements, ctx, v.IPV6, false)
result := make(intfList, 0, len(l)) result := make(intfList, 0, len(l))
for _, el := range l { for _, el := range l {
intf, ok := el.(*routerIntf) intf, ok := el.(*routerIntf)
if !ok { if !ok {
c.err("Unexpected '%s' in %s", el, ctx) c.err("Unexpected '%s' in %s", el, ctx)
} else if intf.router.managed == "" { } else if intf.router.managed == "" {
c.err("Must not reference unmanaged %s in %s", in tf.name, ctx) c.err("Must not reference unmanaged %s in %s", in tf, ctx)
} else { } else {
// Reverse swapped main and virtual interface. // Reverse swapped main and virtual interface.
if main := intf.mainIntf; main != nil { if main := intf.mainIntf; main != nil {
intf = main intf = main
} }
result.push(intf) result.push(intf)
} }
} }
return result return result
} }
skipping to change at line 1060 skipping to change at line 1051
if len(elements) == 0 { if len(elements) == 0 {
return return
} }
c.addPathrestriction(name, elements) c.addPathrestriction(name, elements)
} }
func (c *spoc) setupRouter(v *ast.Router, s *symbolTable) { func (c *spoc) setupRouter(v *ast.Router, s *symbolTable) {
name := v.Name name := v.Name
v6 := v.IPV6 v6 := v.IPV6
r := new(router) r := new(router)
c.allRouters = append(c.allRouters, r)
r.name = name r.name = name
r.ipV6 = v6 r.ipV6 = v6
rName := name[len("router:"):] rName := name[len("router:"):]
if v6 { if v6 {
s.router6[rName] = r s.router6[rName] = r
} else { } else {
s.router[rName] = r s.router[rName] = r
} }
i := strings.Index(rName, "@") i := strings.Index(rName, "@")
if i != -1 { if i != -1 {
r.deviceName = rName[:i] r.deviceName = rName[:i]
r.vrf = rName[i+1:] r.vrf = rName[i+1:]
} else { } else {
r.deviceName = rName r.deviceName = rName
} }
if i != -1 && !isSimpleName(rName[:i]) || !isSimpleName(rName[i+1:]) { if i != -1 && !isSimpleName(rName[:i]) || !isSimpleName(rName[i+1:]) {
c.err("Invalid identifier in definition of '%s'", name) c.err("Invalid identifier in definition of '%s'", name)
} }
noProtectSelf := false noProtectSelf := false
var routingDefault *routing var routingDefault *mcastProto
for _, a := range v.Attributes { for _, a := range v.Attributes {
switch a.Name { switch a.Name {
case "managed": case "managed":
r.managed = c.getManaged(a, name) r.managed = c.getManaged(a, name)
case "filter_only": case "filter_only":
r.filterOnly = c.getIpPrefixList(a, v6, name) r.filterOnly = c.getIpPrefixList(a, v6, name)
case "model": case "model":
r.model = c.getModel(a, name) r.model = c.getModel(a, name)
case "no_group_code": case "no_group_code":
r.noGroupCode = c.getFlag(a, name) r.noGroupCode = c.getFlag(a, name)
skipping to change at line 1199 skipping to change at line 1191
} else if !intf.loopback { } else if !intf.loopback {
intf.routing = routingDefault intf.routing = routingDefault
} }
} }
} }
if rt := intf.routing; rt != nil && intf.ipType == unnumb eredIP { if rt := intf.routing; rt != nil && intf.ipType == unnumb eredIP {
switch rt.name { switch rt.name {
case "manual", "dynamic": case "manual", "dynamic":
default: default:
c.err("Routing '%s' not supported for unn umbered %s", c.err("Routing '%s' not supported for unn umbered %s",
rt.name, intf.name) rt.name, intf)
} }
} }
} }
} }
// Check again after "managed=routing_only" has been removed. // Check again after "managed=routing_only" has been removed.
if managed := r.managed; managed != "" { if managed := r.managed; managed != "" {
if managed == "local" { if managed == "local" {
if r.filterOnly == nil { if r.filterOnly == nil {
c.err("Missing attribute 'filter_only' for %s", n ame) c.err("Missing attribute 'filter_only' for %s", n ame)
skipping to change at line 1475 skipping to change at line 1467
sCtx := a.Name + " of " + name sCtx := a.Name + " of " + name
l := c.getComplexValue(a, name) l := c.getComplexValue(a, name)
for _, a2 := range l { for _, a2 := range l {
switch a2.Name { switch a2.Name {
case "ip": case "ip":
intf.ip = c.getIp(a2, v6, sCtx) intf.ip = c.getIp(a2, v6, sCtx)
default: default:
c.err("Unexpected attribute in %s : %s", sCtx, a2.Name) c.err("Unexpected attribute in %s : %s", sCtx, a2.Name)
} }
} }
if intf.ip == nil { if intf.ip.IsZero() {
c.err("Missing IP in %s", sCtx) c.err("Missing IP in %s", sCtx)
intf.ipType = shortIP intf.ipType = shortIP
} }
secondaryList.push(intf) secondaryList.push(intf)
} else { } else {
c.err("Unexpected attribute in %s: %s", name, a.N ame) c.err("Unexpected attribute in %s: %s", name, a.N ame)
} }
} }
} }
skipping to change at line 1527 skipping to change at line 1519
// Subsequent code becomes simpler if virtual interface is main interface . // Subsequent code becomes simpler if virtual interface is main interface .
if virtual != nil { if virtual != nil {
switch intf.ipType { switch intf.ipType {
case unnumberedIP: case unnumberedIP:
c.err("No virtual IP supported for unnumbered %s", name) c.err("No virtual IP supported for unnumbered %s", name)
case negotiatedIP: case negotiatedIP:
c.err("No virtual IP supported for negotiated %s", name) c.err("No virtual IP supported for negotiated %s", name)
case bridgedIP: case bridgedIP:
c.err("No virtual IP supported for bridged %s", name) c.err("No virtual IP supported for bridged %s", name)
default: default:
if intf.ip != nil { if !intf.ip.IsZero() {
// Move main IP to secondary. // Move main IP to secondary.
secondary := new(routerIntf) secondary := new(routerIntf)
secondary.name = intf.name secondary.name = intf.name
secondary.ip = intf.ip secondary.ip = intf.ip
secondaryList.push(secondary) secondaryList.push(secondary)
// But we need the original main interface // But we need the original main interface
// when handling auto interfaces. // when handling auto interfaces.
intf.origMain = secondary intf.origMain = secondary
skipping to change at line 1672 skipping to change at line 1664
hwName = "unknown" hwName = "unknown"
} }
var hw *hardware var hw *hardware
if hw = hwMap[hwName]; hw != nil { if hw = hwMap[hwName]; hw != nil {
// All logical interfaces of one hardware interface // All logical interfaces of one hardware interface
// need to use the same NAT binding, // need to use the same NAT binding,
// because NAT operates on hardware, not on logic. // because NAT operates on hardware, not on logic.
if !bindNatEq(intf.bindNat, hw.bindNat) { if !bindNatEq(intf.bindNat, hw.bindNat) {
c.err("All logical interfaces of %s\n"+ c.err("All logical interfaces of %s\n"+
" at %s must use identical NAT binding", hwName, r.name) " at %s must use identical NAT binding", hwName, r)
} }
} else { } else {
hw = &hardware{name: hwName, loopback: true} hw = &hardware{name: hwName, loopback: true}
hwMap[hwName] = hw hwMap[hwName] = hw
r.hardware = append(r.hardware, hw) r.hardware = append(r.hardware, hw)
hw.bindNat = intf.bindNat hw.bindNat = intf.bindNat
} }
// Hardware keeps attribute .loopback only if all // Hardware keeps attribute .loopback only if all
// interfaces have attribute .loopback. // interfaces have attribute .loopback.
if !intf.loopback { if !intf.loopback {
skipping to change at line 1698 skipping to change at line 1690
hw.interfaces.push(intf) hw.interfaces.push(intf)
intf.hardware = hw intf.hardware = hw
for _, s := range secondaryList { for _, s := range secondaryList {
s.hardware = hw s.hardware = hw
hw.interfaces.push(s) hw.interfaces.push(s)
} }
// Interface of managed router must not have individual owner, // Interface of managed router must not have individual owner,
// because whole device is managed from one place. // because whole device is managed from one place.
if intf.owner != nil { if intf.owner != nil {
c.warn("Ignoring attribute 'owner' at managed %s", intf.n ame) c.warn("Ignoring attribute 'owner' at managed %s", intf)
intf.owner = nil intf.owner = nil
} }
// Attribute 'vip' only supported at unmanaged router. // Attribute 'vip' only supported at unmanaged router.
if vip { if vip {
c.err("Must not use attribute 'vip' at %s of managed rout er", name) c.err("Must not use attribute 'vip' at %s of managed rout er", name)
} }
// Don't allow 'routing=manual' at single interface, because // Don't allow 'routing=manual' at single interface, because
// approve would remove manual routes otherwise. // approve would remove manual routes otherwise.
// Approve only leaves routes unchanged, if Netspoc generates // Approve only leaves routes unchanged, if Netspoc generates
// no routes at all. // no routes at all.
if rt := intf.routing; rt != nil && rt.name == "manual" { if rt := intf.routing; rt != nil && rt.name == "manual" {
c.warn("'routing=manual' must only be applied to router, not to %s", c.warn("'routing=manual' must only be applied to router, not to %s",
intf.name) intf)
} }
if l := intf.hub; l != nil { if l := intf.hub; l != nil {
if intf.ipType != hasIP { if intf.ipType != hasIP {
c.err("Crypto hub %s must have IP address", intf) c.err("Crypto hub %s must have IP address", intf)
} }
if intf.bindNat != nil { if intf.bindNat != nil {
c.err("Must not use 'bind_nat' at crypto hub %s\n "+ c.err("Must not use 'bind_nat' at crypto hub %s\n "+
" Move 'bind_nat' to crypto definition in stead", intf) " Move 'bind_nat' to crypto definition in stead", intf)
} }
skipping to change at line 1790 skipping to change at line 1782
fullName = intf.name fullName = intf.name
shortName = fullName[len("interface:"):] shortName = fullName[len("interface:"):]
} }
var n *network var n *network
if intf.redundant { if intf.redundant {
n = s.network[shortName] n = s.network[shortName]
} }
if n == nil { if n == nil {
n = new(network) n = new(network)
n.name = fullName n.name = fullName
n.ip = intf.ip n.ipp = netaddr.IPPrefix{IP: intf.ip, Bits: getHostPrefix
n.mask = getHostMask(v6) (v6)}
// Mark as automatically created. // Mark as automatically created.
n.loopback = true n.loopback = true
n.subnetOf = subnetOf n.subnetOf = subnetOf
n.isLayer3 = intf.isLayer3 n.isLayer3 = intf.isLayer3
n.ipV6 = v6 n.ipV6 = v6
// Move NAT definition to loopback network. // Move NAT definition to loopback network.
n.nat = nat n.nat = nat
skipping to change at line 1823 skipping to change at line 1814
if intf.disabled { if intf.disabled {
c.warn(msg, nName, name) c.warn(msg, nName, name)
} else { } else {
c.err(msg, nName, name) c.err(msg, nName, name)
intf.disabled = true intf.disabled = true
} }
} else { } else {
for _, intf := range append(intfList{intf}, secondaryList ...) { for _, intf := range append(intfList{intf}, secondaryList ...) {
intf.network = n intf.network = n
n.interfaces.push(intf) n.interfaces.push(intf)
if intf.ipType != shortIP && !(ipGiven && intf.ip == nil) { if intf.ipType != shortIP && !(ipGiven && intf.ip .IsZero()) {
c.checkInterfaceIp(intf, n) c.checkInterfaceIp(intf, n)
} }
} }
} }
// Non loopback interface must use simple NAT with single IP // Non loopback interface must use simple NAT with single IP
// and without any NAT attributes. // and without any NAT attributes.
if len(nat) != 0 { if len(nat) != 0 {
intf.nat = make(map[string]net.IP) intf.nat = make(map[string]netaddr.IP)
for tag, info := range nat { for tag, info := range nat {
// Reject all non IP NAT attributes. // Reject all non IP NAT attributes.
if info.hidden || info.identity || info.dynamic { if info.hidden || info.identity || info.dynamic {
c.err("Only 'ip' allowed in nat:%s of %s" , tag, intf) c.err("Only 'ip' allowed in nat:%s of %s" , tag, intf)
} else { } else {
intf.nat[tag] = info.ip intf.nat[tag] = info.ipp.IP
} }
} }
} }
} }
for _, intf := range append(intfList{intf}, secondaryList...) { for _, intf := range append(intfList{intf}, secondaryList...) {
// Link interface with router and vice versa. // Link interface with router and vice versa.
r.interfaces.push(intf) r.interfaces.push(intf)
intf.router = r intf.router = r
intf.ipV6 = r.ipV6 intf.ipV6 = r.ipV6
skipping to change at line 1897 skipping to change at line 1888
if c.dateIsReached(sv.disableAt, "'disable_at' of "+name) { if c.dateIsReached(sv.disableAt, "'disable_at' of "+name) {
sv.disabled = true sv.disabled = true
} }
default: default:
c.err("Unexpected attribute in %s: %s", name, a.Name) c.err("Unexpected attribute in %s: %s", name, a.Name)
} }
} }
if sv.overlaps != nil { if sv.overlaps != nil {
sv.overlapsUsed = make(map[*service]bool) sv.overlapsUsed = make(map[*service]bool)
} }
elements := func(a *ast.NamedUnion) []ast.Element {
l := a.Elements
if len(l) == 0 {
c.warn("%s of %s is empty", a.Name, name)
}
return l
}
sv.foreach = v.Foreach sv.foreach = v.Foreach
sv.user = elements(v.User) sv.user = v.User.Elements
for _, v2 := range v.Rules { for _, v2 := range v.Rules {
ru := new(unexpRule) ru := new(unexpRule)
ru.service = sv ru.service = sv
if v2.Deny { if v2.Deny {
ru.action = "deny" ru.action = "deny"
} else { } else {
ru.action = "permit" ru.action = "permit"
} }
ru.src = elements(v2.Src) ru.src = v2.Src.Elements
ru.dst = elements(v2.Dst) ru.dst = v2.Dst.Elements
srcUser := c.checkUserInUnion(ru.src, "'src' of "+name) srcUser := c.checkUserInUnion(ru.src, "'src' of "+name)
dstUser := c.checkUserInUnion(ru.dst, "'dst' of "+name) dstUser := c.checkUserInUnion(ru.dst, "'dst' of "+name)
if !(srcUser || dstUser) { if !(srcUser || dstUser) {
c.err("Each rule of %s must use keyword 'user'", name) c.err("Each rule of %s must use keyword 'user'", name)
} }
if sv.foreach && !(srcUser && dstUser) { if sv.foreach && !(srcUser && dstUser) {
c.warn( c.warn(
"Each rule of %s should reference 'user' in 'src' and 'dst'\n"+ "Each rule of %s should reference 'user' in 'src' and 'dst'\n"+
" because service has keyword 'foreach'", name) " because service has keyword 'foreach'", name)
} }
skipping to change at line 2425 skipping to change at line 2409
continue continue
FAIL: FAIL:
c.err("Unknown extension in '%s' of %s: %s", a.Name, ctx, att) c.err("Unknown extension in '%s' of %s: %s", a.Name, ctx, att)
} }
info.name += add info.name += add
} }
return &info return &info
} }
// Definition of dynamic routing protocols. // Definition of dynamic routing protocols.
var routingInfo = map[string]*routing{ var routingInfo = map[string]*mcastProto{
"EIGRP": &routing{ "EIGRP": &mcastProto{
name: "EIGRP", name: "EIGRP",
prt: &proto{proto: "88", name: "proto 88"}, prt: &proto{proto: "88", name: "proto 88"},
mcast: mcastInfo{v4: []string{"224.0.0.10"}, v6: []string{"ff02:: mcast: mcast{
a"}}, v4: multicast{ips: []string{"224.0.0.10"}},
v6: multicast{ips: []string{"ff02::a"}}},
}, },
"OSPF": &routing{ "OSPF": &mcastProto{
name: "OSPF", name: "OSPF",
prt: &proto{proto: "89", name: "proto 89"}, prt: &proto{proto: "89", name: "proto 89"},
mcast: mcastInfo{v4: []string{"224.0.0.5", "224.0.0.6"}, mcast: mcast{
v6: []string{"ff02::5", "ff02::6"}}, v4: multicast{ips: []string{"224.0.0.5", "224.0.0.6"}},
v6: multicast{ips: []string{"ff02::5", "ff02::6"}}},
}, },
"RIPv2": &routing{ "RIPv2": &mcastProto{
name: "RIP", name: "RIP",
prt: &proto{proto: "udp", ports: [2]int{520, 520}, name: "udp 52 0"}, prt: &proto{proto: "udp", ports: [2]int{520, 520}, name: "udp 52 0"},
mcast: mcastInfo{v4: []string{"224.0.0.9"}, mcast: mcast{
v6: []string{"ff02::9"}}, v4: multicast{ips: []string{"224.0.0.9"}},
v6: multicast{ips: []string{"ff02::9"}}},
}, },
"dynamic": &routing{name: "dynamic"}, "dynamic": &mcastProto{name: "dynamic"},
// Identical to 'dynamic', but must only be applied to router, not // Identical to 'dynamic', but must only be applied to router, not
// to routerIntf. // to routerIntf.
"manual": &routing{name: "manual"}, "manual": &mcastProto{name: "manual"},
} }
func (c *spoc) getRouting(a *ast.Attribute, ctx string) *routing { func (c *spoc) getRouting(a *ast.Attribute, ctx string) *mcastProto {
v := c.getSingleValue(a, ctx) v := c.getSingleValue(a, ctx)
r := routingInfo[v] r := routingInfo[v]
if r == nil { if r == nil {
c.err("Unknown routing protocol in '%s' of %s", a.Name, ctx) c.err("Unknown routing protocol in '%s' of %s", a.Name, ctx)
} }
return r return r
} }
// Definition of redundancy protocols. // Definition of redundancy protocols.
var xxrpInfo = map[string]*xxrp{ var xxrpInfo = map[string]*mcastProto{
"VRRP": &xxrp{ "VRRP": &mcastProto{
prt: &proto{proto: "112", name: "proto 112"}, prt: &proto{proto: "112", name: "proto 112"},
mcast: mcastInfo{v4: []string{"224.0.0.18"}, v6: []string{"ff02:: mcast: mcast{
12"}}, v4: multicast{ips: []string{"224.0.0.18"}},
v6: multicast{ips: []string{"ff02::12"}}},
}, },
"HSRP": &xxrp{ "HSRP": &mcastProto{
prt: &proto{proto: "udp", ports: [2]int{1985, 1985}, name: "udp 1 985"}, prt: &proto{proto: "udp", ports: [2]int{1985, 1985}, name: "udp 1 985"},
mcast: mcastInfo{v4: []string{"224.0.0.2"}, mcast: mcast{
v4: multicast{ips: []string{"224.0.0.2"}},
// No official IPv6 multicast address for HSRP available, // No official IPv6 multicast address for HSRP available,
// therefore using IPv4 equivalent. // therefore using IPv4 equivalent.
v6: []string{"::e000:2"}}, v6: multicast{ips: []string{"::e000:2"}}},
}, },
"HSRPv2": &xxrp{ "HSRPv2": &mcastProto{
prt: &proto{proto: "udp", ports: [2]int{1985, 1985}, name: "udp 1 985"}, prt: &proto{proto: "udp", ports: [2]int{1985, 1985}, name: "udp 1 985"},
mcast: mcastInfo{v4: []string{"224.0.0.102"}, mcast: mcast{
v6: []string{"ff02::66"}}, v4: multicast{ips: []string{"224.0.0.102"}},
v6: multicast{ips: []string{"ff02::66"}}},
}, },
} }
func (c *spoc) getVirtual(a *ast.Attribute, v6 bool, ctx string) *routerIntf { func (c *spoc) getVirtual(a *ast.Attribute, v6 bool, ctx string) *routerIntf {
virtual := new(routerIntf) virtual := new(routerIntf)
virtual.name = ctx + ".virtual" virtual.name = ctx + ".virtual"
virtual.redundant = true virtual.redundant = true
vCtx := "'" + a.Name + "' of " + ctx vCtx := "'" + a.Name + "' of " + ctx
l := c.getComplexValue(a, ctx) l := c.getComplexValue(a, ctx)
for _, a2 := range l { for _, a2 := range l {
switch a2.Name { switch a2.Name {
case "ip": case "ip":
virtual.ip = c.getIp(a2, v6, vCtx) virtual.ip = c.getIp(a2, v6, vCtx)
case "type": case "type":
t := c.getSingleValue(a2, vCtx) t := c.getSingleValue(a2, vCtx)
if _, found := xxrpInfo[t]; !found { p := xxrpInfo[t]
if p == nil {
c.err("Unknown redundancy protocol in %s", vCtx) c.err("Unknown redundancy protocol in %s", vCtx)
} }
virtual.redundancyType = t virtual.redundancyType = p
case "id": case "id":
id := c.getSingleValue(a2, vCtx) id := c.getSingleValue(a2, vCtx)
num, err := strconv.Atoi(id) num, err := strconv.Atoi(id)
if err != nil { if err != nil {
c.err("Redundancy ID must be numeric in %s", vCtx ) c.err("Redundancy ID must be numeric in %s", vCtx )
} else if !(num >= 0 || num < 256) { } else if !(num >= 0 || num < 256) {
c.err("Redundancy ID must be < 256 in %s", vCtx) c.err("Redundancy ID must be < 256 in %s", vCtx)
} }
virtual.redundancyId = id virtual.redundancyId = id
default: default:
c.err("Unexpected attribute in %s: %s", vCtx, a2.Name) c.err("Unexpected attribute in %s: %s", vCtx, a2.Name)
} }
} }
if virtual.ip == nil { if virtual.ip.IsZero() {
c.err("Missing IP in %s", vCtx) c.err("Missing IP in %s", vCtx)
return nil return nil
} }
if virtual.redundancyId != "" && virtual.redundancyType == "" { if virtual.redundancyId != "" && virtual.redundancyType == nil {
c.err("Redundancy ID is given without redundancy protocol in %s", c.err("Redundancy ID is given without redundancy protocol in %s",
vCtx) vCtx)
} }
return virtual return virtual
} }
func isDomain(n string) bool { func isDomain(n string) bool {
for _, part := range strings.Split(n, ".") { for _, part := range strings.Split(n, ".") {
if !isSimpleName(part) { if !isSimpleName(part) {
return false return false
skipping to change at line 2548 skipping to change at line 2541
if !(i > 0 && isDomain(id[:i]) && isDomain(id[i+1:])) { if !(i > 0 && isDomain(id[:i]) && isDomain(id[i+1:])) {
c.err("Invalid '%s' in %s: %s", a.Name, ctx, id) c.err("Invalid '%s' in %s: %s", a.Name, ctx, id)
} }
return id return id
} }
func isSimpleName(n string) bool { func isSimpleName(n string) bool {
return n != "" && strings.IndexAny(n, ".:/@") == -1 return n != "" && strings.IndexAny(n, ".:/@") == -1
} }
func (c *spoc) getIp(a *ast.Attribute, v6 bool, ctx string) net.IP { func (c *spoc) getIp(a *ast.Attribute, v6 bool, ctx string) netaddr.IP {
return c.convIP(c.getSingleValue(a, ctx), v6, a.Name, ctx) return c.convIP(c.getSingleValue(a, ctx), v6, a.Name, ctx)
} }
func (c *spoc) getIpList(a *ast.Attribute, v6 bool, ctx string) []net.IP { func (c *spoc) getIpList(a *ast.Attribute, v6 bool, ctx string) []netaddr.IP {
var result []net.IP var result []netaddr.IP
for _, v := range c.getValueList(a, ctx) { for _, v := range c.getValueList(a, ctx) {
result = append(result, c.convIP(v, v6, a.Name, ctx)) result = append(result, c.convIP(v, v6, a.Name, ctx))
} }
return result return result
} }
func (c *spoc) getIpRange(a *ast.Attribute, v6 bool, ctx string) [2]net.IP { func (c *spoc) getIpRange(
a *ast.Attribute, v6 bool, ctx string) netaddr.IPRange {
v := c.getSingleValue(a, ctx) v := c.getSingleValue(a, ctx)
l := strings.Split(v, " - ") l := strings.Split(v, " - ")
var result [2]net.IP var result netaddr.IPRange
if len(l) != 2 { if len(l) != 2 {
c.err("Expected IP range in '%s' of %s", a.Name, ctx) c.err("Expected IP range in '%s' of %s", a.Name, ctx)
} else { } else {
result[0] = c.convIP(l[0], v6, a.Name, ctx) result.From = c.convIP(l[0], v6, a.Name, ctx)
result[1] = c.convIP(l[1], v6, a.Name, ctx) result.To = c.convIP(l[1], v6, a.Name, ctx)
if !result.Valid() {
c.err("Invalid IP range in %s", ctx)
}
} }
return result return result
} }
func (c *spoc) getIpPrefix(a *ast.Attribute, v6 bool, ctx string) (net.IP, net.I func (c *spoc) getIpPrefix(
PMask) { a *ast.Attribute, v6 bool, ctx string) netaddr.IPPrefix {
v := c.getSingleValue(a, ctx) v := c.getSingleValue(a, ctx)
n := c.convIpPrefix(v, v6, a.Name, ctx) return c.convIpPrefix(v, v6, a.Name, ctx)
if n == nil {
return nil, nil
}
return n.IP, n.Mask
} }
func (c *spoc) getIpPrefixList( func (c *spoc) getIpPrefixList(
a *ast.Attribute, v6 bool, ctx string) []*net.IPNet { a *ast.Attribute, v6 bool, ctx string) []netaddr.IPPrefix {
var result []*net.IPNet var result []netaddr.IPPrefix
for _, v := range c.getValueList(a, ctx) { for _, v := range c.getValueList(a, ctx) {
result = append(result, c.convIpPrefix(v, v6, a.Name, ctx)) result = append(result, c.convIpPrefix(v, v6, a.Name, ctx))
} }
return result return result
} }
func (c *spoc) convIpPrefix(s string, v6 bool, name, ctx string) *net.IPNet { func (c *spoc) convIpPrefix(
ip, n, err := net.ParseCIDR(s) s string, v6 bool, name, ctx string) netaddr.IPPrefix {
n, err := netaddr.ParseIPPrefix(s)
if err != nil { if err != nil {
c.err("%s in '%s' of %s", err, name, ctx) c.err("Invalid CIDR address: %s in '%s' of %s", s, name, ctx)
return nil } else if n.Masked() != n {
}
if !n.IP.Equal(ip) {
c.err("IP and mask of %s don't match in '%s' of %s", s, name, ctx ) c.err("IP and mask of %s don't match in '%s' of %s", s, name, ctx )
} }
n.IP = c.getVxIP(n.IP, v6, name, ctx) c.checkVxIP(n.IP, v6, name, ctx)
return n return n
} }
func (c *spoc) convIP(s string, v6 bool, name, ctx string) net.IP { func (c *spoc) convIP(s string, v6 bool, name, ctx string) netaddr.IP {
ip := net.ParseIP(s) ip, err := netaddr.ParseIP(s)
if ip == nil { if err != nil {
c.err("Invalid IP address in '%s' of %s: %s", name, ctx, s) c.err("Invalid IP address in '%s' of %s", name, ctx)
return nil return ip
} }
return c.getVxIP(ip, v6, name, ctx) c.checkVxIP(ip, v6, name, ctx)
return ip
} }
func (c *spoc) getVxIP(ip net.IP, v6 bool, name, ctx string) net.IP { func (c *spoc) checkVxIP(ip netaddr.IP, v6 bool, name, ctx string) {
v4IP := ip.To4()
if v6 { if v6 {
if v4IP != nil { if ip.Is4() {
c.err("IPv6 address expected in '%s' of %s", name, ctx) c.err("IPv6 address expected in '%s' of %s", name, ctx)
} }
return ip } else if ip.Is6() {
} else if v4IP == nil {
c.err("IPv4 address expected in '%s' of %s", name, ctx) c.err("IPv4 address expected in '%s' of %s", name, ctx)
} }
return v4IP
} }
// Check if given date has been reached already. // Check if given date has been reached already.
var dateRegex = regexp.MustCompile(`^(\d\d\d\d-\d\d-\d\d)$`) var dateRegex = regexp.MustCompile(`^(\d\d\d\d-\d\d-\d\d)$`)
func (c *spoc) dateIsReached(s, ctx string) bool { func (c *spoc) dateIsReached(s, ctx string) bool {
l := dateRegex.FindStringSubmatch(s) l := dateRegex.FindStringSubmatch(s)
if l == nil { if l == nil {
c.err("Date expected as yyyy-mm-dd in %s", ctx) c.err("Date expected as yyyy-mm-dd in %s", ctx)
return false return false
skipping to change at line 2727 skipping to change at line 2721
c.err("Typed name expected in '%s' of %s", a.Name, ctx) c.err("Typed name expected in '%s' of %s", a.Name, ctx)
return "", "" return "", ""
} }
return v[:i], v[i+1:] return v[:i], v[i+1:]
} }
func (c *spoc) getRealOwnerRef(a *ast.Attribute, s *symbolTable, ctx string) *ow ner { func (c *spoc) getRealOwnerRef(a *ast.Attribute, s *symbolTable, ctx string) *ow ner {
o := c.tryOwnerRef(a, s, ctx) o := c.tryOwnerRef(a, s, ctx)
if o != nil { if o != nil {
if o.admins == nil { if o.admins == nil {
c.err("Missing attribute 'admins' in %s of %s", o.name, c tx) c.err("Missing attribute 'admins' in %s of %s", o, ctx)
o.admins = make([]string, 0) o.admins = make([]string, 0)
} }
if o.onlyWatch { if o.onlyWatch {
c.err("%s with attribute 'only_watch' must only be used a t area,\n"+ c.err("%s with attribute 'only_watch' must only be used a t area,\n"+
" not at %s", o.name, ctx) " not at %s", o, ctx)
o.onlyWatch = false o.onlyWatch = false
} }
} }
return o return o
} }
func (c *spoc) tryOwnerRef(a *ast.Attribute, s *symbolTable, ctx string) *owner { func (c *spoc) tryOwnerRef(a *ast.Attribute, s *symbolTable, ctx string) *owner {
name := c.getIdentifier(a, ctx) name := c.getIdentifier(a, ctx)
o := s.owner[name] o := s.owner[name]
if o == nil { if o == nil {
skipping to change at line 3078 skipping to change at line 3072
func (c *spoc) addNetNat(a *ast.Attribute, m map[string]*network, v6 bool, func (c *spoc) addNetNat(a *ast.Attribute, m map[string]*network, v6 bool,
s *symbolTable, ctx string) map[string]*network { s *symbolTable, ctx string) map[string]*network {
return c.addXNat(a, m, v6, s, ctx, c.getIpPrefix) return c.addXNat(a, m, v6, s, ctx, c.getIpPrefix)
} }
func (c *spoc) addIntfNat(a *ast.Attribute, m map[string]*network, v6 bool, func (c *spoc) addIntfNat(a *ast.Attribute, m map[string]*network, v6 bool,
s *symbolTable, ctx string) map[string]*network { s *symbolTable, ctx string) map[string]*network {
return c.addXNat(a, m, v6, s, ctx, return c.addXNat(a, m, v6, s, ctx,
func(a *ast.Attribute, v6 bool, ctx string) (net.IP, net.IPMask) { func(a *ast.Attribute, v6 bool, ctx string) netaddr.IPPrefix {
ip := c.getSingleValue(a, ctx) ip := c.getSingleValue(a, ctx)
return c.convIP(ip, v6, a.Name, ctx), getHostMask(v6) return netaddr.IPPrefix{
IP: c.convIP(ip, v6, a.Name, ctx),
Bits: getHostPrefix(v6),
}
}) })
} }
func (c *spoc) addXNat( func (c *spoc) addXNat(
a *ast.Attribute, m map[string]*network, v6 bool, s *symbolTable, ctx str ing, a *ast.Attribute, m map[string]*network, v6 bool, s *symbolTable, ctx str ing,
getIpX func(*ast.Attribute, bool, string) (net.IP, net.IPMask), getIpX func(*ast.Attribute, bool, string) netaddr.IPPrefix,
) map[string]*network { ) map[string]*network {
if !strings.HasPrefix(a.Name, "nat:") { if !strings.HasPrefix(a.Name, "nat:") {
return nil return nil
} }
_, tag := c.splitCheckTypedName(a.Name) _, tag := c.splitCheckTypedName(a.Name)
nat := new(network) nat := new(network)
natCtx := a.Name + " of " + ctx natCtx := a.Name + " of " + ctx
l := c.getComplexValue(a, ctx) l := c.getComplexValue(a, ctx)
for _, a2 := range l { for _, a2 := range l {
switch a2.Name { switch a2.Name {
case "ip": case "ip":
nat.ip, nat.mask = getIpX(a2, v6, natCtx) nat.ipp = getIpX(a2, v6, natCtx)
case "hidden": case "hidden":
nat.hidden = c.getFlag(a2, natCtx) nat.hidden = c.getFlag(a2, natCtx)
case "identity": case "identity":
nat.identity = c.getFlag(a2, natCtx) nat.identity = c.getFlag(a2, natCtx)
case "dynamic": case "dynamic":
nat.dynamic = c.getFlag(a2, natCtx) nat.dynamic = c.getFlag(a2, natCtx)
case "subnet_of": case "subnet_of":
nat.subnetOf = c.tryNetworkRef(a2, s, v6, natCtx) nat.subnetOf = c.tryNetworkRef(a2, s, v6, natCtx)
default: default:
c.err("Unexpected attribute in %s: %s", natCtx, a2.Name) c.err("Unexpected attribute in %s: %s", natCtx, a2.Name)
skipping to change at line 3124 skipping to change at line 3121
if a2.Name != "hidden" { if a2.Name != "hidden" {
c.err("Hidden NAT must not use attribute '%s' in %s", c.err("Hidden NAT must not use attribute '%s' in %s",
a2.Name, natCtx) a2.Name, natCtx)
} }
} }
// This simplifies error checks for overlapping addresses. // This simplifies error checks for overlapping addresses.
nat.dynamic = true nat.dynamic = true
// Provide an unusable address. // Provide an unusable address.
nat.ip = getZeroIp(v6) nat.ipp = netaddr.IPPrefix{IP: getZeroIp(v6), Bits: getHostPrefix
nat.mask = getHostMask(v6) (v6)}
} else if nat.identity { } else if nat.identity {
for _, a2 := range l { for _, a2 := range l {
if a2.Name != "identity" { if a2.Name != "identity" {
c.err("Identity NAT must not use attribute '%s' i n %s", c.err("Identity NAT must not use attribute '%s' i n %s",
a2.Name, natCtx) a2.Name, natCtx)
} }
} }
nat.dynamic = true nat.dynamic = true
} else if nat.ip == nil { } else if nat.ipp.IsZero() {
c.err("Missing IP address in %s", natCtx) c.err("Missing IP address in %s", natCtx)
} }
// Attribute .natTag is used later to look up static translation // Attribute .natTag is used later to look up static translation
// of hosts inside a dynamically translated network. // of hosts inside a dynamically translated network.
nat.natTag = tag nat.natTag = tag
nat.name = ctx nat.name = ctx
nat.descr = "nat:" + tag + " of " + ctx nat.descr = "nat:" + tag + " of " + ctx
if m == nil { if m == nil {
m = make(map[string]*network) m = make(map[string]*network)
} }
m[tag] = nat m[tag] = nat
return m return m
} }
func (c *spoc) addIPNat(a *ast.Attribute, m map[string]net.IP, v6 bool, func (c *spoc) addIPNat(a *ast.Attribute, m map[string]netaddr.IP, v6 bool,
ctx string) map[string]net.IP { ctx string) map[string]netaddr.IP {
if !strings.HasPrefix(a.Name, "nat:") { if !strings.HasPrefix(a.Name, "nat:") {
return nil return nil
} }
_, name := c.splitCheckTypedName(a.Name) _, name := c.splitCheckTypedName(a.Name)
var ip net.IP var ip netaddr.IP
natCtx := a.Name + " of " + ctx natCtx := a.Name + " of " + ctx
l := c.getComplexValue(a, ctx) l := c.getComplexValue(a, ctx)
for _, a2 := range l { for _, a2 := range l {
switch a2.Name { switch a2.Name {
case "ip": case "ip":
ip = c.getIp(a2, v6, natCtx) ip = c.getIp(a2, v6, natCtx)
default: default:
c.err("Unexpected attribute in %s: %s", natCtx, a2.Name) c.err("Unexpected attribute in %s: %s", natCtx, a2.Name)
} }
} }
if m == nil { if m == nil {
m = make(map[string]net.IP) m = make(map[string]netaddr.IP)
} }
m[name] = ip m[name] = ip
return m return m
} }
func (c *spoc) checkInterfaceIp(intf *routerIntf, n *network) { func (c *spoc) checkInterfaceIp(intf *routerIntf, n *network) {
if intf.ipType == unnumberedIP { if intf.ipType == unnumberedIP {
if n.ipType != unnumberedIP { if n.ipType != unnumberedIP {
c.err("Unnumbered %s must not be linked to %s", intf, n) c.err("Unnumbered %s must not be linked to %s", intf, n)
} }
skipping to change at line 3195 skipping to change at line 3191
return return
} }
if intf.ipType != hasIP { if intf.ipType != hasIP {
// Nothing to be checked: attribute 'bridged' is set automaticall y // Nothing to be checked: attribute 'bridged' is set automaticall y
// for an interface without IP and linked to bridged network. // for an interface without IP and linked to bridged network.
return return
} }
// Check compatibility of interface IP and network IP/mask. // Check compatibility of interface IP and network IP/mask.
ip := intf.ip ip := intf.ip
nIP := n.ip if !n.ipp.Contains(ip) {
mask := n.mask
if !matchIp(ip, nIP, mask) {
c.err("%s's IP doesn't match %s's IP/mask", intf, n) c.err("%s's IP doesn't match %s's IP/mask", intf, n)
} }
if isHostMask(mask) { if n.ipp.IsSingleIP() {
c.warn("%s has address of its network.\n"+ c.warn("%s has address of its network.\n"+
" Remove definition of %s and\n"+ " Remove definition of %s and\n"+
" add attribute 'loopback' at interface definition.", " add attribute 'loopback' at interface definition.",
intf, n) intf, n)
} else if !n.ipV6 { } else if !n.ipV6 {
// Check network and broadcast address only for IPv4, // Check network and broadcast address only for IPv4,
// but not for /31 IPv4 (see RFC 3021). // but not for /31 IPv4 (see RFC 3021).
len, _ := mask.Size() if n.ipp.Bits != 31 {
if len != 31 { if ip == n.ipp.IP {
if bytes.Compare(ip, nIP) == 0 {
c.err("%s has address of its network", intf) c.err("%s has address of its network", intf)
} }
if bytes.Compare(ip, getBroadcastIP(n)) == 0 { if ip == n.ipp.Range().To {
c.err("%s has broadcast address", intf) c.err("%s has broadcast address", intf)
} }
} }
} }
} }
//############################################################################ //############################################################################
// Purpose : Moves attribute 'no_in_acl' from interface to hardware because // Purpose : Moves attribute 'no_in_acl' from interface to hardware because
// ACLs operate on hardware, not on logic. Marks hardware needing // ACLs operate on hardware, not on logic. Marks hardware needing
// outgoing ACLs. // outgoing ACLs.
skipping to change at line 3253 skipping to change at line 3246
// Prevent duplicate error message. // Prevent duplicate error message.
if hw.noInAcl { if hw.noInAcl {
continue continue
} }
hw.noInAcl = true hw.noInAcl = true
// Assure max number of main interfaces at no_in_acl-hardware == 1. // Assure max number of main interfaces at no_in_acl-hardware == 1.
if nonSecondaryIntfCount(hw.interfaces) != 1 { if nonSecondaryIntfCount(hw.interfaces) != 1 {
c.err("Only one logical interface allowed at hardware '%s ' of %s\n"+ c.err("Only one logical interface allowed at hardware '%s ' of %s\n"+
" because of attribute 'no_in_acl'", hw.name, r.n ame) " because of attribute 'no_in_acl'", hw.name, r)
} }
count++ count++
// Reference no_in_acl interface in router attribute. // Reference no_in_acl interface in router attribute.
r.noInAcl = intf r.noInAcl = intf
} }
if count == 0 { if count == 0 {
return return
} }
skipping to change at line 3319 skipping to change at line 3312
func (c *spoc) moveLockedIntf(intf *routerIntf) { func (c *spoc) moveLockedIntf(intf *routerIntf) {
orig := intf.router orig := intf.router
// Use different and uniqe name for each split router. // Use different and uniqe name for each split router.
name := "router:" + intf.name[len("interface:"):] name := "router:" + intf.name[len("interface:"):]
new := *orig new := *orig
new.name = name new.name = name
new.origRouter = orig new.origRouter = orig
new.interfaces = intfList{intf} new.interfaces = intfList{intf}
intf.router = &new intf.router = &new
c.routerFragments = append(c.routerFragments, &new) c.allRouters = append(c.allRouters, &new)
// Don't check fragment for reachability. // Don't check fragment for reachability.
new.policyDistributionPoint = nil new.policyDistributionPoint = nil
// Remove interface from old router. // Remove interface from old router.
// Retain original interfaces. // Retain original interfaces.
l := orig.interfaces l := orig.interfaces
if orig.origIntfs == nil { if orig.origIntfs == nil {
orig.origIntfs = l orig.origIntfs = l
} }
skipping to change at line 3370 skipping to change at line 3363
for k, v := range m { for k, v := range m {
m2[k] = v m2[k] = v
} }
new.radiusAttributes = m2 new.radiusAttributes = m2
} }
} }
} }
// Link tunnel networks with tunnel hubs. // Link tunnel networks with tunnel hubs.
func (c *spoc) linkTunnels(s *symbolTable) { func (c *spoc) linkTunnels(s *symbolTable) {
// ToDo: Check if sorting is only needed for deterministic error messages . // Sorting needed for deterministic error messages.
sorted := make([]*crypto, 0, len(symTable.crypto)) sorted := make([]*crypto, 0, len(symTable.crypto))
for _, c := range symTable.crypto { for _, c := range symTable.crypto {
sorted = append(sorted, c) sorted = append(sorted, c)
} }
sort.Slice(sorted, func(i, j int) bool { sort.Slice(sorted, func(i, j int) bool {
return sorted[i].name < sorted[j].name return sorted[i].name < sorted[j].name
}) })
for _, cr := range sorted { for _, cr := range sorted {
realHub := cr.hub realHub := cr.hub
if realHub == nil || realHub.disabled { if realHub == nil || realHub.disabled {
skipping to change at line 3442 skipping to change at line 3435
spoke.peer = hub spoke.peer = hub
r.interfaces.push(hub) r.interfaces.push(hub)
hw.interfaces.push(hub) hw.interfaces.push(hub)
spokeNet.interfaces.push(hub) spokeNet.interfaces.push(hub)
// We need hub also be available in orig_interfaces. // We need hub also be available in orig_interfaces.
if r.origIntfs != nil { if r.origIntfs != nil {
r.origIntfs.push(hub) r.origIntfs.push(hub)
} }
if realSpoke.ip == nil { if realSpoke.ip.IsZero() {
if !(model.doAuth || model.canDynCrypto) { if !(model.doAuth || model.canDynCrypto) {
c.err( c.err(
"%s can't establish crypto tunnel to %s with unknown IP", "%s can't establish crypto tunnel to %s with unknown IP",
r, realSpoke) r, realSpoke)
} }
} }
} }
} }
} }
skipping to change at line 3464 skipping to change at line 3457
// - be connected to the same network and // - be connected to the same network and
// - having the same IP address. // - having the same IP address.
// Link all virtual interfaces to the group of member interfaces. // Link all virtual interfaces to the group of member interfaces.
// Check consistency: // Check consistency:
// - Member interfaces must use identical protocol and identical ID. // - Member interfaces must use identical protocol and identical ID.
// - The same ID must not be used by some other group // - The same ID must not be used by some other group
// - connected to the same network // - connected to the same network
// - emploing the same redundancy type // - emploing the same redundancy type
func (c *spoc) linkVirtualInterfaces() { func (c *spoc) linkVirtualInterfaces() {
// Collect array of virtual interfaces with same IP at same network. // Collect virtual interfaces with same IP at same network.
type key1 struct { type key1 struct {
n *network n *network
ip string ip netaddr.IP
} }
net2ip2virtual := make(map[key1]intfList) netIP2virtual := make(map[key1]intfList)
// Map to look up first virtual interface of a group // Look up virtual interface of a group inside the same network and
// inside the same network and using the same ID and type. // using the same ID and type.
type key2 struct { type key2 struct {
n *network n *network
id string id string
typ string typ *mcastProto
} }
net2id2type2virtual := make(map[key2]*routerIntf) netIdType2virtual := make(map[key2]*routerIntf)
for _, v1 := range c.virtualInterfaces { for _, v1 := range c.virtualInterfaces {
if v1.disabled { if v1.disabled {
continue continue
} }
ip := v1.ip.String() ip := v1.ip
n := v1.network n := v1.network
t1 := v1.redundancyType t1 := v1.redundancyType
id1 := v1.redundancyId id1 := v1.redundancyId
k := key1{n, ip} k1 := key1{n, ip}
l := net2ip2virtual[k] l := netIP2virtual[k1]
if l != nil { if l != nil {
v2 := l[0] v2 := l[0]
t2 := v2.redundancyType t2 := v2.redundancyType
if t1 != t2 { if t1 != t2 {
c.err("Must use identical redundancy protocol at\ n"+ c.err("Must use identical redundancy protocol at\ n"+
" - %s\n"+ " - %s\n"+
" - %s", v2, v1) " - %s", v2, v1)
} }
id2 := v2.redundancyId id2 := v2.redundancyId
if id1 != id2 { if id1 != id2 {
c.err("Must use identical ID at\n"+ c.err("Must use identical ID at\n"+
" - %s\n"+ " - %s\n"+
" - %s", v2, v1) " - %s", v2, v1)
} }
} else { } else if id1 != "" {
// Check for identical ID used at unrelated virtual inter faces // Check for identical ID used at unrelated virtual inter faces
// inside the same network. // inside the same network.
if id1 != "" { k2 := key2{n, id1, t1}
if v2 := net2id2type2virtual[key2{n, id1, t1}]; v if v2 := netIdType2virtual[k2]; v2 != nil {
2 != nil { c.err("Must use different ID at unrelated\n"+
c.err("Must use different ID at unrelated " - %s\n"+
\n"+ " - %s", v2, v1)
" - %s\n"+ } else {
" - %s", v2, v1) netIdType2virtual[k2] = v1
} else {
net2id2type2virtual[key2{n, id1, t1}] = v
1
}
} }
} }
l.push(v1) l.push(v1)
net2ip2virtual[k] = l netIP2virtual[k1] = l
} }
for _, l := range net2ip2virtual { for _, l := range netIP2virtual {
for _, intf := range l { for _, intf := range l {
intf.redundancyIntfs = l intf.redundancyIntfs = l
} }
} }
// Automatically add pathrestriction to each group of virtual // Automatically add pathrestriction to each group of virtual
// interfaces, where at least one interface is managed. // interfaces, where at least one interface is managed.
// Pathrestriction would be useless if all devices are unmanaged. // Pathrestriction would be useless if all devices are unmanaged.
for _, l := range net2ip2virtual { for _, l := range netIP2virtual {
if len(l) < 2 { if len(l) < 2 {
continue continue
} }
for _, intf := range l { for _, intf := range l {
r := intf.router r := intf.router
if r.managed != "" || r.routingOnly { if r.managed != "" || r.routingOnly {
name := "auto-virtual-" + intf.ip.String() name := "auto-virtual-" + intf.ip.String()
c.addPathrestriction(name, l) c.addPathrestriction(name, l)
break break
} }
skipping to change at line 3559 skipping to change at line 3551
//debug("%s at %s", name, intf) //debug("%s at %s", name, intf)
// Multiple restrictions may be applied to a single interface. // Multiple restrictions may be applied to a single interface.
intf.pathRestrict = append(intf.pathRestrict, pr) intf.pathRestrict = append(intf.pathRestrict, pr)
// Unmanaged router with pathrestriction is handled specially. // Unmanaged router with pathrestriction is handled specially.
// It is separating zones, but gets no code. // It is separating zones, but gets no code.
if intf.router.managed == "" { if intf.router.managed == "" {
intf.router.semiManaged = true intf.router.semiManaged = true
} }
} }
} }
// If a pathrestriction or a bind_nat is added to an unmanged router,
// it is marked as semiManaged. As a consequence, a new zone would be
// created at each interface of this router.
// If an unmanaged router has a large number of interfaces, but only
// one or a few pathrestrictions or bind_nat attached, we would get a
// large number of useless zones.
// To reduce the number of newly created zones, we split an unmanaged
// router with pathrestrictions or bind_nat, if it has more than two
// interfaces without any pathrestriction or bind_nat:
// - original part having only interfaces without pathrestriction or bind_nat,
// - one split part for each interface with pathrestriction or bind_nat.
// All parts are connected by a freshly created unnumbered network.
func (c *spoc) splitSemiManagedRouter() {
for _, r := range getIpv4Ipv6Routers() {
// Unmanaged router is marked as semi_managed, if
// - it has pathrestriction,
// - it has interface with bind_nat or
// - is managed=routing_only.
if !r.semiManaged {
continue
}
// Don't split device with 'managed=routing_only'.
if r.routingOnly {
continue
}
// Count interfaces without pathrestriction or bind_nat.
count := 0
for _, intf := range r.interfaces {
if intf.mainIntf == nil &&
intf.pathRestrict == nil &&
intf.bindNat == nil {
count++
}
}
if count < 2 {
continue
}
// Retain copy of original interfaces for finding [all] interface
s.
if r.origIntfs == nil {
r.origIntfs = append(r.origIntfs, r.interfaces...)
}
// Split router into two or more parts.
// Move each interface with pathrestriction or bind_nat and
// corresponding secondary interface to new router.
for i, intf := range r.interfaces {
if intf.pathRestrict == nil && intf.bindNat == nil ||
intf.mainIntf != nil {
continue
}
// Create new semiManged router with identical name.
// Add reference to original router having 'origIntfs'.
nr := new(router)
nr.name = r.name
nr.semiManaged = true
nr.origRouter = r
intf.router = nr
c.routerFragments = append(c.routerFragments, nr)
// Link current and newly created router by unnumbered ne
twork.
// Add reference to original interface at internal interf
ace.
iName := intf.name
n := new(network)
n.name = iName + "(split Network)"
n.ipType = unnumberedIP
intf1 := new(routerIntf)
intf1.name = iName + "(split1)"
intf1.ipType = unnumberedIP
intf1.router = r
intf1.network = n
intf2 := new(routerIntf)
intf2.name = iName + "(split2)"
intf2.ipType = unnumberedIP
intf2.router = nr
intf2.network = n
n.interfaces = intfList{intf1, intf2}
nr.interfaces = intfList{intf2, intf}
// Add reference to other interface at original interface
// at newly created router. This is needed for post
// processing in checkPathrestrictions.
if intf.pathRestrict != nil {
intf.splitOther = intf2
}
// Replace original interface at current router.
r.interfaces[i] = intf1
}
// Original router is no longer semiManged.
r.semiManaged = false
// Move secondary interfaces, modify original list.
l := r.interfaces
j := 0
for _, intf := range l {
if m := intf.mainIntf; m != nil {
nr := m.router
if nr != r {
intf.router = nr
nr.interfaces.push(intf)
continue
}
}
l[j] = intf
j++
}
r.interfaces = l[:j]
}
}
 End of changes. 102 change blocks. 
168 lines changed or deleted 157 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)