From 95536869403c88e1b4478c4dcc2a3edd918bf3dc Mon Sep 17 00:00:00 2001 From: mame82 Date: Fri, 11 May 2018 13:57:39 +0000 Subject: [PATCH] Added DHCP server and client support for service. ToDo: CLI implementation for DHCP modes --- service/DHCPSrv.go | 65 ----------- service/defaults.go | 19 +++- service/dhcp.go | 259 ++++++++++++++++++++++++++++++++++++++++++++ service/network.go | 54 ++++++++- 4 files changed, 326 insertions(+), 71 deletions(-) delete mode 100644 service/DHCPSrv.go create mode 100644 service/dhcp.go diff --git a/service/DHCPSrv.go b/service/DHCPSrv.go deleted file mode 100644 index 4316940..0000000 --- a/service/DHCPSrv.go +++ /dev/null @@ -1,65 +0,0 @@ -package service - -import ( - pb "../proto" - "fmt" - "io/ioutil" - "os" -) - -/* -The DHCP server part relies on "dnsmasq" and thus depends on the binary -Note: dnsmasq default option have to be disabled explicitly if not needed, by setting an empty value (1, 3, 6, 12, 28) - */ - -func defaultLeaseFile(s *pb.DHCPServerSettings) (lf string) { - return fmt.Sprintf("/tmp/dnsmasq_%s.leases", s.ListenInterface) //default lease file -} - -func DHCPCreateConfigFile(s *pb.DHCPServerSettings, filename string) (err error) { - file_content, err := DHCPCreateConfigFileString(s) - if err != nil {return} - err = ioutil.WriteFile(filename, []byte(file_content), os.ModePerm) - return -} - -func DHCPCreateConfigFileString(s *pb.DHCPServerSettings) (config string, err error) { - config = fmt.Sprintf("interface=%s\n", s.ListenInterface) - //bind only o the given interface, except suppressed by `doNotBindInterface` option - if !s.DoNotBindInterface { config += fmt.Sprintf("bind-interfaces\n") } - config += fmt.Sprintf("port=%d\n", s.ListenPort) - if len(s.CallbackScript) > 0 { config += fmt.Sprintf("dhcp-script=%s\n", s.CallbackScript) } - if len(s.LeaseFile) > 0 { - config += fmt.Sprintf("dhcp-leasefile=%s\n", s.LeaseFile) - } else { - config += fmt.Sprintf("dhcp-leasefile=%s\n", defaultLeaseFile(s)) //default lease file - } - - - //Iterate over Ranges - for _, pRange := range s.Ranges { - config += fmt.Sprintf("dhcp-range=%s,%s,%s\n", pRange.RangeLower, pRange.RangeUpper, pRange.LeaseTime) - } - - //Iterate over options - // - // Note: for duplicates only the last one should be used, but not in any case, see - // https://developers.google.com/protocol-buffers/docs/proto3#maps) - // - // "... When parsing from the wire or when merging, if there are duplicate map keys the last key seen is used. - // When parsing a map from text format, parsing may fail if there are duplicate keys...." - for o_num, o_val := range s.Options { - o_str := fmt.Sprintf("dhcp-option=%d", o_num) - if len(o_val) > 0 { - o_str += fmt.Sprintf(",%s\n", o_val) - } else { - o_str += "\n" - } - config += o_str - } - config += fmt.Sprintf("log-dhcp\n") //extensive logging by default - if (!s.NotAuthoritative) { config += fmt.Sprintf("dhcp-authoritative\n") } - - return -} - diff --git a/service/defaults.go b/service/defaults.go index 0a6f0e1..ee1d62a 100644 --- a/service/defaults.go +++ b/service/defaults.go @@ -7,19 +7,28 @@ import ( func GetDefaultNetworkSettingsUSB() (*pb.EthernetInterfaceSettings) { //configure 172.24.0.1/255.255.255.252 for usbeth ifSettings := &pb.EthernetInterfaceSettings { - Enabled: false, + Enabled: true, Name: USB_ETHERNET_BRIDGE_NAME, IpAddress4: "172.16.0.1", Netmask4: "255.255.255.252", - Mode: pb.EthernetInterfaceSettings_MANUAL, + Mode: pb.EthernetInterfaceSettings_DHCP_SERVER, DhcpServerSettings: GetDefaultDHCPConfigUSB(), } return ifSettings } +func GetDefaultNetworkSettingsWiFi() (*pb.EthernetInterfaceSettings) { + ifSettings := &pb.EthernetInterfaceSettings { + Enabled: true, + Name: "wlan0", + Mode: pb.EthernetInterfaceSettings_DHCP_CLIENT, + } + return ifSettings +} + func GetDefaultDHCPConfigUSB() (settings *pb.DHCPServerSettings) { settings = &pb.DHCPServerSettings{ - CallbackScript: "/bin/evilscript", + //CallbackScript: "/bin/evilscript", DoNotBindInterface: false, //only bind to given interface ListenInterface: USB_ETHERNET_BRIDGE_NAME, LeaseFile: "/tmp/dnsmasq_" + USB_ETHERNET_BRIDGE_NAME + ".leases", @@ -47,14 +56,14 @@ func GetDefaultLEDSettings() (res pb.LEDSettings) { func GetDefaultGadgetSettings() (res pb.GadgetSettings) { res = pb.GadgetSettings{ - Enabled: false, + Enabled: true, Vid: "0x1d6b", Pid: "0x1337", Manufacturer: "MaMe82", Product: "P4wnP1 by MaMe82", Serial: "deadbeef1337", Use_CDC_ECM: false, - Use_RNDIS: false, + Use_RNDIS: true, Use_HID_KEYBOARD: false, Use_HID_MOUSE: false, Use_HID_RAW: false, diff --git a/service/dhcp.go b/service/dhcp.go new file mode 100644 index 0000000..49866ed --- /dev/null +++ b/service/dhcp.go @@ -0,0 +1,259 @@ +package service + +import ( + pb "../proto" + "fmt" + "io/ioutil" + "os" + "os/exec" + "log" + "errors" + "strconv" + "syscall" + "strings" +) + +/* +The DHCP server part relies on "dnsmasq" and thus depends on the binary +Note: dnsmasq default option have to be disabled explicitly if not needed, by setting an empty value (1, 3, 6, 12, 28) + +The DHCP client relies on dhcpcd binary +*/ + +func pidFileDHCPSrv(nameIface string) string { + return fmt.Sprintf("/var/run/dnsmasq_%s.pid", nameIface) +} + +func pidFileDHCPClient(nameIface string) string { + return fmt.Sprintf("/var/run/dhcpcd-%s.pid", nameIface) +} + +func leaseFileDHCPSrv(s *pb.DHCPServerSettings) (lf string) { + return fmt.Sprintf("/tmp/dnsmasq_%s.leases", s.ListenInterface) //default lease file +} + + +func NameConfigFileDHCPSrv(nameIface string) string { + return fmt.Sprintf("/tmp/dnsmasq_%s.conf", nameIface) +} + + +func StartDHCPClient(nameIface string) (err error) { + log.Printf("Starting DHCP client for interface '%s'...\n", nameIface) + + //check if interface is valid + if_exists,_ := CheckInterfaceExistence(nameIface) + if !if_exists { + return errors.New(fmt.Sprintf("The given interface '%s' doesn't exist", nameIface)) + } + + + //We use the run command and allow dhcpcd to daemonize + proc := exec.Command("/sbin/dhcpcd", nameIface) + dhcpcd_out, err := proc.CombinedOutput() + //err = proc.Run() + if err != nil { return err} + fmt.Printf("Dhcpcd output for %s:\n%s", nameIface, dhcpcd_out) + + + log.Printf("... DHCP client for interface '%s' started\n", nameIface) + return nil +} + +func IsDHCPClientRunning(nameIface string) (running bool, pid int, err error) { + if_exists,_ := CheckInterfaceExistence(nameIface) + if !if_exists { + return false, 0, errors.New(fmt.Sprintf("The given interface '%s' doesn't exist", nameIface)) + } + + pid_file := pidFileDHCPClient(nameIface) + + //Check if the pidFile exists + if _, err := os.Stat(pid_file); os.IsNotExist(err) { + return false, 0,nil //file doesn't exist, so we assume dhcpcd isn't running + } + + //File exists, read the PID + content, err := ioutil.ReadFile(pid_file) + if err != nil { return false, 0, err} + pid, err = strconv.Atoi(strings.TrimSuffix(string(content), "\n")) + if err != nil { return false, 0, errors.New(fmt.Sprintf("Error parsing PID file %s: %v", pid_file, err))} + + //With PID given, check if the process is indeed running (pid_file could stay, even if the process has died already) + err_kill := syscall.Kill(pid, 0) //sig 0: doesn't send a signal, but error checking is still performed + switch err_kill{ + case nil: + //ToDo: Check if the running process image is indeed dhcpcd + return true, pid, nil //Process is running + case syscall.ESRCH: + //Process doesn't exist + return false, pid, nil + case syscall.EPERM: + //process exists, but we have no access permission + return true, pid, err_kill + default: + return false, pid, err_kill + } +} + + +func StopDHCPClient(nameIface string) (err error) { + log.Printf("Stoping DHCP client for interface '%s'...\n", nameIface) + + //check if interface is valid + if_exists,_ := CheckInterfaceExistence(nameIface) + if !if_exists { + return errors.New(fmt.Sprintf("The given interface '%s' doesn't exist", nameIface)) + } + + + //We use the run command and allow dhcpcd to daemonize + proc := exec.Command("/sbin/dhcpcd", "-x", nameIface) + dhcpcd_out, err := proc.CombinedOutput() + //err = proc.Run() + if err != nil { return err} + fmt.Printf("Dhcpcd out for %s:\n%s", nameIface, dhcpcd_out) + + + log.Printf("... DHCP client for interface '%s' stopped\n", nameIface) + return nil +} + +func StartDHCPServer(nameIface string, configPath string) (err error) { + log.Printf("Starting DHCP server for interface '%s' with config '%s'...\n", nameIface, configPath) + + //check if interface is valid + if_exists,_ := CheckInterfaceExistence(nameIface) + if !if_exists { + return errors.New(fmt.Sprintf("The given interface '%s' doesn't exist", nameIface)) + } + + //Check if there's already a DHCP server running for the given interface + running, _, err_r := IsDHCPServerRunning(nameIface) + if err != nil { return errors.New(fmt.Sprintf("Error fetching state of DHCP server: %v\n", err_r)) } + if running {return errors.New(fmt.Sprintf("Error starting DHCP server for interface '%s', there is already a DHCP server running\n", nameIface))} + + //We use the run command and allow dnsmasq to daemonize + proc := exec.Command("/usr/sbin/dnsmasq", "-x", pidFileDHCPSrv(nameIface), "-C", configPath) + //dnsmasq_out, err := proc.CombinedOutput() + err = proc.Run() + if err != nil { return err} + //fmt.Printf("Dnsmasq out %s\n", dnsmasq_out) + + + log.Printf("... DHCP server for interface '%s' started\n", nameIface) + return nil +} + + +func IsDHCPServerRunning(nameIface string) (running bool, pid int, err error) { + if_exists,_ := CheckInterfaceExistence(nameIface) + if !if_exists { + return false, 0, errors.New(fmt.Sprintf("The given interface '%s' doesn't exist", nameIface)) + } + + pid_file := pidFileDHCPSrv(nameIface) + + //Check if the pidFile exists + if _, err := os.Stat(pid_file); os.IsNotExist(err) { + return false, 0,nil //file doesn't exist, so we assume dnsmasq isn't running + } + + //File exists, read the PID + content, err := ioutil.ReadFile(pid_file) + if err != nil { return false, 0, err} + pid, err = strconv.Atoi(strings.TrimSuffix(string(content), "\n")) + if err != nil { return false, 0, errors.New(fmt.Sprintf("Error parsing PID file %s: %v", pid_file, err))} + + //With PID given, check if the process is indeed running (pid_file could stay, even if the dnsmasq process has died already) + err_kill := syscall.Kill(pid, 0) //sig 0: doesn't send a signal, but error checking is still performed + switch err_kill{ + case nil: + //ToDo: Check if the running process image is indeed dnsmasq + return true, pid, nil //Process is running + case syscall.ESRCH: + //Process doesn't exist + return false, pid, nil + case syscall.EPERM: + //process exists, but we have no access permission + return true, pid, err_kill + default: + return false, pid, err_kill + } +} + +func StopDHCPServer(nameIface string) (err error) { + //don't check if interface is valid, to allow closing of orphaned DHCP procs (interface went down while running) + log.Printf("Stopping DHCP server for interface '%s' ...\n", nameIface) + running,pid,err := IsDHCPServerRunning(nameIface) + if err != nil { return } + + if running { + //send SIGTERM + err = syscall.Kill(pid, syscall.SIGTERM) + if err != nil { return } + } else { + log.Printf("... DHCP server for interface '%s' wasn't started\n", nameIface) + } + + running,pid,err = IsDHCPServerRunning(nameIface) + if err != nil { return } + if (running) { + log.Printf("... couldn't terminate DHCP server for interface '%s'\n", nameIface) + } else { + log.Printf("... DHCP server for interface '%s' stopped\n", nameIface) + } + + //Delete PID file + os.Remove(pidFileDHCPSrv(nameIface)) + return nil +} + + +func DHCPCreateConfigFile(s *pb.DHCPServerSettings, filename string) (err error) { + file_content, err := DHCPCreateConfigFileString(s) + if err != nil {return} + err = ioutil.WriteFile(filename, []byte(file_content), os.ModePerm) + return +} + +func DHCPCreateConfigFileString(s *pb.DHCPServerSettings) (config string, err error) { + config = fmt.Sprintf("interface=%s\n", s.ListenInterface) + //bind only o the given interface, except suppressed by `doNotBindInterface` option + if !s.DoNotBindInterface { config += fmt.Sprintf("bind-interfaces\n") } + config += fmt.Sprintf("port=%d\n", s.ListenPort) + if len(s.CallbackScript) > 0 { config += fmt.Sprintf("dhcp-script=%s\n", s.CallbackScript) } + if len(s.LeaseFile) > 0 { + config += fmt.Sprintf("dhcp-leasefile=%s\n", s.LeaseFile) + } else { + config += fmt.Sprintf("dhcp-leasefile=%s\n", leaseFileDHCPSrv(s)) //default lease file + } + + + //Iterate over Ranges + for _, pRange := range s.Ranges { + config += fmt.Sprintf("dhcp-range=%s,%s,%s\n", pRange.RangeLower, pRange.RangeUpper, pRange.LeaseTime) + } + + //Iterate over options + // + // Note: for duplicates only the last one should be used, but not in any case, see + // https://developers.google.com/protocol-buffers/docs/proto3#maps) + // + // "... When parsing from the wire or when merging, if there are duplicate map keys the last key seen is used. + // When parsing a map from text format, parsing may fail if there are duplicate keys...." + for o_num, o_val := range s.Options { + o_str := fmt.Sprintf("dhcp-option=%d", o_num) + if len(o_val) > 0 { + o_str += fmt.Sprintf(",%s\n", o_val) + } else { + o_str += "\n" + } + config += o_str + } + config += fmt.Sprintf("log-dhcp\n") //extensive logging by default + if (!s.NotAuthoritative) { config += fmt.Sprintf("dhcp-authoritative\n") } + + return +} + diff --git a/service/network.go b/service/network.go index 03216ea..67d5d33 100644 --- a/service/network.go +++ b/service/network.go @@ -21,6 +21,12 @@ func InitDefaultNetworkSettings() (err error) { err = ConfigureInterface(GetDefaultNetworkSettingsUSB()) if err != nil { return } } + + wifiEthActive, _ := CheckInterfaceExistence("wlan0") + if wifiEthActive { + err = ConfigureInterface(GetDefaultNetworkSettingsWiFi()) + if err != nil { return } + } return } @@ -112,6 +118,12 @@ func ConfigureInterface(settings *pb.EthernetInterfaceSettings) (err error) { iface, err := net.InterfaceByName(settings.Name) if err != nil { return err } + //stop DHCP server / client if still running + running, _, err := IsDHCPServerRunning(settings.Name) + if (err == nil) && running {StopDHCPServer(settings.Name)} + running, _, err = IsDHCPClientRunning(settings.Name) + if (err == nil) && running {StopDHCPClient(settings.Name)} + switch settings.Mode { case pb.EthernetInterfaceSettings_MANUAL: //Generate net @@ -124,7 +136,6 @@ func ConfigureInterface(settings *pb.EthernetInterfaceSettings) (err error) { log.Printf("Setting Interface %s to IP %s\n", iface.Name, settings.IpAddress4) netlink.NetworkLinkAddIp(iface, net.ParseIP(settings.IpAddress4), ipNet) - //ToDo: Disabling doesn't work in gadget settings, but from test.go if settings.Enabled { log.Printf("Setting Interface %s to UP\n", iface.Name) err = netlink.NetworkLinkUp(iface) @@ -133,6 +144,47 @@ func ConfigureInterface(settings *pb.EthernetInterfaceSettings) (err error) { err = netlink.NetworkLinkDown(iface) } if err != nil { return err } + case pb.EthernetInterfaceSettings_DHCP_SERVER: + //Generate net + ipNet, err := IpNetFromIPv4AndNetmask(settings.IpAddress4, settings.Netmask4) + if err != nil { return err } + + //Flush old IPs + netlink.NetworkLinkFlush(iface) + //set IP + log.Printf("Setting Interface %s to IP %s\n", iface.Name, settings.IpAddress4) + netlink.NetworkLinkAddIp(iface, net.ParseIP(settings.IpAddress4), ipNet) + + if settings.Enabled { + log.Printf("Setting Interface %s to UP\n", iface.Name) + err = netlink.NetworkLinkUp(iface) + + //check DhcpServerSettings + if settings.DhcpServerSettings == nil { + err = errors.New(fmt.Sprintf("Ethernet configuration for interface %s is set to DHCP Server mode, but doesn't provide DhcpServerSettings", settings.Name)) + log.Println(err) + return err + } + ifName := settings.Name + confName := NameConfigFileDHCPSrv(ifName) + err = DHCPCreateConfigFile(settings.DhcpServerSettings, confName) + if err != nil {return err} + //stop already running DHCPServers for the interface + StopDHCPServer(ifName) + //start the DHCP server + err = StartDHCPServer(ifName, confName) + if err != nil {return err} + } else { + log.Printf("Setting Interface %s to DOWN\n", iface.Name) + err = netlink.NetworkLinkDown(iface) + } + if err != nil { return err } + case pb.EthernetInterfaceSettings_DHCP_CLIENT: + netlink.NetworkLinkFlush(iface) + if settings.Enabled { + StartDHCPClient(settings.Name) + } + } return nil