github.com/coreos/mantle@v0.13.0/platform/local/dnsmasq.go (about)

     1  // Copyright 2015 CoreOS, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package local
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"text/template"
    21  
    22  	"github.com/coreos/pkg/capnslog"
    23  	"github.com/vishvananda/netlink"
    24  
    25  	"github.com/coreos/mantle/system/exec"
    26  	"github.com/coreos/mantle/util"
    27  )
    28  
    29  type Interface struct {
    30  	HardwareAddr net.HardwareAddr
    31  	DHCPv4       []net.IPNet
    32  	DHCPv6       []net.IPNet
    33  	//SLAAC net.IPAddr
    34  }
    35  
    36  type Segment struct {
    37  	BridgeName string
    38  	BridgeIf   *Interface
    39  	Interfaces []*Interface
    40  	nextIf     int
    41  }
    42  
    43  type Dnsmasq struct {
    44  	Segments []*Segment
    45  	dnsmasq  *exec.ExecCmd
    46  }
    47  
    48  const (
    49  	numInterfaces = 500 // affects dnsmasq startup time
    50  	numSegments   = 1
    51  
    52  	debugConfig = `
    53  log-queries
    54  log-dhcp
    55  `
    56  
    57  	quietConfig = `
    58  quiet-dhcp
    59  quiet-dhcp6
    60  quiet-ra
    61  `
    62  
    63  	commonConfig = `
    64  keep-in-foreground
    65  leasefile-ro
    66  log-facility=-
    67  pid-file=
    68  
    69  no-resolv
    70  no-hosts
    71  enable-ra
    72  
    73  # point NTP at this host (0.0.0.0 and :: are special)
    74  dhcp-option=option:ntp-server,0.0.0.0
    75  dhcp-option=option6:ntp-server,[::]
    76  
    77  {{range .Segments}}
    78  domain={{.BridgeName}}.local
    79  
    80  {{range .BridgeIf.DHCPv4}}
    81  dhcp-range={{.IP}},static
    82  {{end}}
    83  
    84  {{range .BridgeIf.DHCPv6}}
    85  dhcp-range={{.IP}},ra-names,slaac
    86  {{end}}
    87  
    88  {{range .Interfaces}}
    89  dhcp-host={{.HardwareAddr}}{{template "ips" .DHCPv4}}{{template "ips" .DHCPv6}}
    90  {{end}}
    91  {{end}}
    92  
    93  {{define "ips"}}{{range .}}{{printf ",%s" .IP}}{{end}}{{end}}
    94  `
    95  )
    96  
    97  var plog = capnslog.NewPackageLogger("github.com/coreos/mantle", "platform/local")
    98  
    99  func newInterface(s byte, i uint16) *Interface {
   100  	return &Interface{
   101  		HardwareAddr: net.HardwareAddr{0x02, s, 0, 0, byte(i / 256), byte(i % 256)},
   102  		DHCPv4: []net.IPNet{{
   103  			IP:   net.IP{10, s, byte(i / 256), byte(i % 256)},
   104  			Mask: net.CIDRMask(16, 32)}},
   105  		DHCPv6: []net.IPNet{{
   106  			IP:   net.IP{0xfd, s, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, byte(i / 256), byte(i % 256)},
   107  			Mask: net.CIDRMask(64, 128)}},
   108  	}
   109  }
   110  
   111  func newSegment(s byte) (*Segment, error) {
   112  	seg := &Segment{
   113  		BridgeName: fmt.Sprintf("br%d", s),
   114  		BridgeIf:   newInterface(s, 1),
   115  	}
   116  
   117  	for i := uint16(2); i < 2+numInterfaces; i++ {
   118  		seg.Interfaces = append(seg.Interfaces, newInterface(s, i))
   119  	}
   120  
   121  	br := netlink.Bridge{
   122  		LinkAttrs: netlink.LinkAttrs{
   123  			Name:         seg.BridgeName,
   124  			HardwareAddr: seg.BridgeIf.HardwareAddr,
   125  		},
   126  	}
   127  
   128  	if err := netlink.LinkAdd(&br); err != nil {
   129  		return nil, fmt.Errorf("LinkAdd() failed: %v", err)
   130  	}
   131  
   132  	for _, addr := range seg.BridgeIf.DHCPv4 {
   133  		nladdr := netlink.Addr{IPNet: &addr}
   134  		if err := netlink.AddrAdd(&br, &nladdr); err != nil {
   135  			return nil, fmt.Errorf("DHCPv4 AddrAdd() failed: %v", err)
   136  		}
   137  	}
   138  
   139  	for _, addr := range seg.BridgeIf.DHCPv6 {
   140  		nladdr := netlink.Addr{IPNet: &addr}
   141  		if err := netlink.AddrAdd(&br, &nladdr); err != nil {
   142  			return nil, fmt.Errorf("DHCPv6 AddrAdd() failed: %v", err)
   143  		}
   144  	}
   145  
   146  	if err := netlink.LinkSetUp(&br); err != nil {
   147  		return nil, fmt.Errorf("LinkSetUp() failed: %v", err)
   148  	}
   149  
   150  	return seg, nil
   151  }
   152  
   153  func NewDnsmasq() (*Dnsmasq, error) {
   154  	dm := &Dnsmasq{}
   155  	for s := byte(0); s < numSegments; s++ {
   156  		seg, err := newSegment(s)
   157  		if err != nil {
   158  			return nil, fmt.Errorf("Network setup failed: %v", err)
   159  		}
   160  		dm.Segments = append(dm.Segments, seg)
   161  	}
   162  
   163  	// setup lo
   164  	lo, err := netlink.LinkByName("lo")
   165  	if err != nil {
   166  		return nil, fmt.Errorf("Network loopback setup failed: %v", err)
   167  	}
   168  	err = netlink.LinkSetUp(lo)
   169  	if err != nil {
   170  		return nil, fmt.Errorf("Network loopback setup failed: %v", err)
   171  	}
   172  
   173  	dm.dnsmasq = exec.Command("dnsmasq", "--conf-file=-")
   174  	cfg, err := dm.dnsmasq.StdinPipe()
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	out, err := dm.dnsmasq.StdoutPipe()
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	dm.dnsmasq.Stderr = dm.dnsmasq.Stdout
   183  	go util.LogFrom(capnslog.INFO, out)
   184  
   185  	if err = dm.dnsmasq.Start(); err != nil {
   186  		cfg.Close()
   187  		return nil, err
   188  	}
   189  
   190  	var configTemplate *template.Template
   191  
   192  	if plog.LevelAt(capnslog.DEBUG) {
   193  		configTemplate = template.Must(
   194  			template.New("dnsmasq").Parse(debugConfig + commonConfig))
   195  	} else {
   196  		configTemplate = template.Must(
   197  			template.New("dnsmasq").Parse(quietConfig + commonConfig))
   198  	}
   199  
   200  	if err = configTemplate.Execute(cfg, dm); err != nil {
   201  		cfg.Close()
   202  		dm.Destroy()
   203  		return nil, err
   204  	}
   205  	cfg.Close()
   206  
   207  	return dm, nil
   208  }
   209  
   210  func (dm *Dnsmasq) GetInterface(bridge string) (in *Interface) {
   211  	for _, seg := range dm.Segments {
   212  		if bridge == seg.BridgeName {
   213  			if seg.nextIf >= len(seg.Interfaces) {
   214  				panic("Not enough interfaces!")
   215  			}
   216  			in = seg.Interfaces[seg.nextIf]
   217  			seg.nextIf++
   218  			return
   219  		}
   220  	}
   221  	panic("Not a valid bridge!")
   222  }
   223  
   224  func (dm *Dnsmasq) Destroy() {
   225  	if err := dm.dnsmasq.Kill(); err != nil {
   226  		plog.Errorf("Error killing dnsmasq: %v", err)
   227  	}
   228  }