|
@@ -131,10 +131,9 @@ type RtMsg struct {
|
|
|
syscall.RtMsg
|
|
|
}
|
|
|
|
|
|
-func newRtMsg(family int) *RtMsg {
|
|
|
+func newRtMsg() *RtMsg {
|
|
|
return &RtMsg{
|
|
|
RtMsg: syscall.RtMsg{
|
|
|
- Family: uint8(family),
|
|
|
Table: syscall.RT_TABLE_MAIN,
|
|
|
Scope: syscall.RT_SCOPE_UNIVERSE,
|
|
|
Protocol: syscall.RTPROT_BOOT,
|
|
@@ -367,40 +366,118 @@ done:
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-// Add a new default gateway. Identical to:
|
|
|
-// ip route add default via $ip
|
|
|
-func AddDefaultGw(ip net.IP) error {
|
|
|
+// Add a new route table entry.
|
|
|
+func AddRoute(destination, source, gateway, device string) error {
|
|
|
+ if destination == "" && source == "" && gateway == "" {
|
|
|
+ return fmt.Errorf("one of destination, source or gateway must not be blank")
|
|
|
+ }
|
|
|
+
|
|
|
s, err := getNetlinkSocket()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
defer s.Close()
|
|
|
|
|
|
- family := getIpFamily(ip)
|
|
|
-
|
|
|
wb := newNetlinkRequest(syscall.RTM_NEWROUTE, syscall.NLM_F_CREATE|syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
|
|
|
+ msg := newRtMsg()
|
|
|
+ currentFamily := -1
|
|
|
+ var rtAttrs []*RtAttr
|
|
|
|
|
|
- msg := newRtMsg(family)
|
|
|
- wb.AddData(msg)
|
|
|
+ if destination != "" {
|
|
|
+ destIP, destNet, err := net.ParseCIDR(destination)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("destination CIDR %s couldn't be parsed", destination)
|
|
|
+ }
|
|
|
+ destFamily := getIpFamily(destIP)
|
|
|
+ currentFamily = destFamily
|
|
|
+ destLen, bits := destNet.Mask.Size()
|
|
|
+ if destLen == 0 && bits == 0 {
|
|
|
+ return fmt.Errorf("destination CIDR %s generated a non-canonical Mask", destination)
|
|
|
+ }
|
|
|
+ msg.Family = uint8(destFamily)
|
|
|
+ msg.Dst_len = uint8(destLen)
|
|
|
+ var destData []byte
|
|
|
+ if destFamily == syscall.AF_INET {
|
|
|
+ destData = destIP.To4()
|
|
|
+ } else {
|
|
|
+ destData = destIP.To16()
|
|
|
+ }
|
|
|
+ rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_DST, destData))
|
|
|
+ }
|
|
|
|
|
|
- var ipData []byte
|
|
|
- if family == syscall.AF_INET {
|
|
|
- ipData = ip.To4()
|
|
|
- } else {
|
|
|
- ipData = ip.To16()
|
|
|
+ if source != "" {
|
|
|
+ srcIP, srcNet, err := net.ParseCIDR(source)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("source CIDR %s couldn't be parsed", source)
|
|
|
+ }
|
|
|
+ srcFamily := getIpFamily(srcIP)
|
|
|
+ if currentFamily != -1 && currentFamily != srcFamily {
|
|
|
+ return fmt.Errorf("source and destination ip were not the same IP family")
|
|
|
+ }
|
|
|
+ currentFamily = srcFamily
|
|
|
+ srcLen, bits := srcNet.Mask.Size()
|
|
|
+ if srcLen == 0 && bits == 0 {
|
|
|
+ return fmt.Errorf("source CIDR %s generated a non-canonical Mask", source)
|
|
|
+ }
|
|
|
+ msg.Family = uint8(srcFamily)
|
|
|
+ msg.Src_len = uint8(srcLen)
|
|
|
+ var srcData []byte
|
|
|
+ if srcFamily == syscall.AF_INET {
|
|
|
+ srcData = srcIP.To4()
|
|
|
+ } else {
|
|
|
+ srcData = srcIP.To16()
|
|
|
+ }
|
|
|
+ rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_SRC, srcData))
|
|
|
}
|
|
|
|
|
|
- gateway := newRtAttr(syscall.RTA_GATEWAY, ipData)
|
|
|
+ if gateway != "" {
|
|
|
+ gwIP := net.ParseIP(gateway)
|
|
|
+ if gwIP == nil {
|
|
|
+ return fmt.Errorf("gateway IP %s couldn't be parsed", gateway)
|
|
|
+ }
|
|
|
+ gwFamily := getIpFamily(gwIP)
|
|
|
+ if currentFamily != -1 && currentFamily != gwFamily {
|
|
|
+ return fmt.Errorf("gateway, source, and destination ip were not the same IP family")
|
|
|
+ }
|
|
|
+ msg.Family = uint8(gwFamily)
|
|
|
+ var gwData []byte
|
|
|
+ if gwFamily == syscall.AF_INET {
|
|
|
+ gwData = gwIP.To4()
|
|
|
+ } else {
|
|
|
+ gwData = gwIP.To16()
|
|
|
+ }
|
|
|
+ rtAttrs = append(rtAttrs, newRtAttr(syscall.RTA_GATEWAY, gwData))
|
|
|
+ }
|
|
|
|
|
|
- wb.AddData(gateway)
|
|
|
+ wb.AddData(msg)
|
|
|
+ for _, attr := range rtAttrs {
|
|
|
+ wb.AddData(attr)
|
|
|
+ }
|
|
|
|
|
|
- if err := s.Send(wb); err != nil {
|
|
|
+ var (
|
|
|
+ native = nativeEndian()
|
|
|
+ b = make([]byte, 4)
|
|
|
+ )
|
|
|
+ iface, err := net.InterfaceByName(device)
|
|
|
+ if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
+ native.PutUint32(b, uint32(iface.Index))
|
|
|
+
|
|
|
+ wb.AddData(newRtAttr(syscall.RTA_OIF, b))
|
|
|
|
|
|
+ if err := s.Send(wb); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
return s.HandleAck(wb.Seq)
|
|
|
}
|
|
|
|
|
|
+// Add a new default gateway. Identical to:
|
|
|
+// ip route add default via $ip
|
|
|
+func AddDefaultGw(ip, device string) error {
|
|
|
+ return AddRoute("", "", ip, device)
|
|
|
+}
|
|
|
+
|
|
|
// Bring up a particular network interface
|
|
|
func NetworkLinkUp(iface *net.Interface) error {
|
|
|
s, err := getNetlinkSocket()
|