mirror of
https://github.com/RoganDawes/P4wnP1_aloa.git
synced 2025-04-14 15:09:02 +02:00
Added DHCP server and client support for service. ToDo: CLI implementation for DHCP modes
This commit is contained in:
parent
ef57825312
commit
9553686940
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
259
service/dhcp.go
Normal file
259
service/dhcp.go
Normal file
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user