Merge: DHCP: check/set static IP
Close #686 * commit '828d3121be807daa8f839dfa7a7ac4ba8a6e7cd8': * client: show message if there is no static ip * client: rename constant * hasStaticIP: use properly named boolean variable + client: static_ip warnings * client: error text * client: disable DHCP check if server enabled and hide errors on disable * client: hide error if DHCP enabled and require check DHCP before enabling * client: accordion styles - client: fix DHCP fields validation * client: fix DHCP error message + config: set default parameters for DHCP server + /control/dhcp/set_config: set static IP + /control/dhcp/find_active_dhcp: detect static IP on Linux * /control/dhcp/find_active_dhcp: new JSON response format
This commit is contained in:
commit
08bedacf0a
|
@ -32,7 +32,11 @@
|
||||||
"dhcp_ip_addresses": "IP addresses",
|
"dhcp_ip_addresses": "IP addresses",
|
||||||
"dhcp_table_hostname": "Hostname",
|
"dhcp_table_hostname": "Hostname",
|
||||||
"dhcp_table_expires": "Expires",
|
"dhcp_table_expires": "Expires",
|
||||||
"dhcp_warning": "If you want to enable the built-in DHCP server, make sure that there is no other active DHCP server. Otherwise, it can break the internet for connected devices!",
|
"dhcp_warning": "If you want to enable DHCP server anyway, make sure that there is no other active DHCP server in your network. Otherwise, it can break the Internet for connected devices!",
|
||||||
|
"dhcp_error": "We could not determine whether there is another DHCP server in the network.",
|
||||||
|
"dhcp_static_ip_error": "In order to use DHCP server a static IP address must be set. We failed to determine if this network interface is configured using static IP address. Please set a static IP address manually.",
|
||||||
|
"dhcp_dynamic_ip_found": "Your system uses dynamic IP address configuration for interface <0>{{interfaceName}}</0>. In order to use DHCP server a static IP address must be set. Your current IP address is <0>{{ipAddress}}</0>. We will automatically set this IP address as static if you press Enable DHCP button.",
|
||||||
|
"error_details": "Error details",
|
||||||
"back": "Back",
|
"back": "Back",
|
||||||
"dashboard": "Dashboard",
|
"dashboard": "Dashboard",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
|
|
|
@ -3,10 +3,12 @@ import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { Trans, withNamespaces } from 'react-i18next';
|
import { Trans, withNamespaces } from 'react-i18next';
|
||||||
|
|
||||||
|
import { DHCP_STATUS_RESPONSE } from '../../../helpers/constants';
|
||||||
import Form from './Form';
|
import Form from './Form';
|
||||||
import Leases from './Leases';
|
import Leases from './Leases';
|
||||||
import Interface from './Interface';
|
import Interface from './Interface';
|
||||||
import Card from '../../ui/Card';
|
import Card from '../../ui/Card';
|
||||||
|
import Accordion from '../../ui/Accordion';
|
||||||
|
|
||||||
class Dhcp extends Component {
|
class Dhcp extends Component {
|
||||||
handleFormSubmit = (values) => {
|
handleFormSubmit = (values) => {
|
||||||
|
@ -19,11 +21,12 @@ class Dhcp extends Component {
|
||||||
|
|
||||||
getToggleDhcpButton = () => {
|
getToggleDhcpButton = () => {
|
||||||
const {
|
const {
|
||||||
config, active, processingDhcp, processingConfig,
|
config, check, processingDhcp, processingConfig,
|
||||||
} = this.props.dhcp;
|
} = this.props.dhcp;
|
||||||
const activeDhcpFound = active && active.found;
|
const otherDhcpFound =
|
||||||
|
check && check.otherServer && check.otherServer.found === DHCP_STATUS_RESPONSE.YES;
|
||||||
const filledConfig = Object.keys(config).every((key) => {
|
const filledConfig = Object.keys(config).every((key) => {
|
||||||
if (key === 'enabled') {
|
if (key === 'enabled' || key === 'icmp_timeout_msec') {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +53,8 @@ class Dhcp extends Component {
|
||||||
onClick={() => this.handleToggle(config)}
|
onClick={() => this.handleToggle(config)}
|
||||||
disabled={
|
disabled={
|
||||||
!filledConfig
|
!filledConfig
|
||||||
|| activeDhcpFound
|
|| !check
|
||||||
|
|| otherDhcpFound
|
||||||
|| processingDhcp
|
|| processingDhcp
|
||||||
|| processingConfig
|
|| processingConfig
|
||||||
}
|
}
|
||||||
|
@ -60,33 +64,89 @@ class Dhcp extends Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getActiveDhcpMessage = () => {
|
getActiveDhcpMessage = (t, check) => {
|
||||||
const { active } = this.props.dhcp;
|
const { found } = check.otherServer;
|
||||||
|
|
||||||
if (active) {
|
|
||||||
if (active.error) {
|
|
||||||
return (
|
|
||||||
<div className="text-danger mb-2">
|
|
||||||
{active.error}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (found === DHCP_STATUS_RESPONSE.ERROR) {
|
||||||
return (
|
return (
|
||||||
<div className="mb-2">
|
<div className="text-danger mb-2">
|
||||||
{active.found ? (
|
<Trans>dhcp_error</Trans>
|
||||||
<div className="text-danger">
|
<div className="mt-2 mb-2">
|
||||||
<Trans>dhcp_found</Trans>
|
<Accordion label={t('error_details')}>
|
||||||
</div>
|
<span>{check.otherServer.error}</span>
|
||||||
) : (
|
</Accordion>
|
||||||
<div className="text-secondary">
|
</div>
|
||||||
<Trans>dhcp_not_found</Trans>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-2">
|
||||||
|
{found === DHCP_STATUS_RESPONSE.YES ? (
|
||||||
|
<div className="text-danger">
|
||||||
|
<Trans>dhcp_found</Trans>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-secondary">
|
||||||
|
<Trans>dhcp_not_found</Trans>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDhcpWarning = (check) => {
|
||||||
|
if (check.otherServer.found === DHCP_STATUS_RESPONSE.NO) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="text-danger">
|
||||||
|
<Trans>dhcp_warning</Trans>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStaticIpWarning = (t, check, interfaceName) => {
|
||||||
|
if (check.staticIP.static === DHCP_STATUS_RESPONSE.ERROR) {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<div className="text-danger mb-2">
|
||||||
|
<Trans>dhcp_static_ip_error</Trans>
|
||||||
|
<div className="mt-2 mb-2">
|
||||||
|
<Accordion label={t('error_details')}>
|
||||||
|
<span>{check.staticIP.error}</span>
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr className="mt-4 mb-4"/>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
check.staticIP.static === DHCP_STATUS_RESPONSE.NO
|
||||||
|
&& check.staticIP.ip
|
||||||
|
&& interfaceName
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<div className="text-secondary mb-2">
|
||||||
|
<Trans
|
||||||
|
components={[
|
||||||
|
<strong key="0">example</strong>,
|
||||||
|
]}
|
||||||
|
values={{
|
||||||
|
interfaceName,
|
||||||
|
ipAddress: check.staticIP.ip,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
dhcp_dynamic_ip_found
|
||||||
|
</Trans>
|
||||||
|
</div>
|
||||||
|
<hr className="mt-4 mb-4"/>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,17 +191,21 @@ class Dhcp extends Component {
|
||||||
this.props.findActiveDhcp(dhcp.config.interface_name)
|
this.props.findActiveDhcp(dhcp.config.interface_name)
|
||||||
}
|
}
|
||||||
disabled={
|
disabled={
|
||||||
!dhcp.config.interface_name
|
dhcp.config.enabled
|
||||||
|
|| !dhcp.config.interface_name
|
||||||
|| dhcp.processingConfig
|
|| dhcp.processingConfig
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Trans>check_dhcp_servers</Trans>
|
<Trans>check_dhcp_servers</Trans>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{this.getActiveDhcpMessage()}
|
{!enabled && dhcp.check &&
|
||||||
<div className="text-danger">
|
<Fragment>
|
||||||
<Trans>dhcp_warning</Trans>
|
{this.getStaticIpWarning(t, dhcp.check, interface_name)}
|
||||||
</div>
|
{this.getActiveDhcpMessage(t, dhcp.check)}
|
||||||
|
{this.getDhcpWarning(dhcp.check)}
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
.accordion {
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion__label {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
padding-left: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion__label:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 7px;
|
||||||
|
left: 0;
|
||||||
|
width: 17px;
|
||||||
|
height: 10px;
|
||||||
|
background-image: url("./svg/chevron-down.svg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion__label--open:after {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion__content {
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import './Accordion.css';
|
||||||
|
|
||||||
|
class Accordion extends Component {
|
||||||
|
state = {
|
||||||
|
isOpen: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick = () => {
|
||||||
|
this.setState(prevState => ({ isOpen: !prevState.isOpen }));
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const accordionClass = this.state.isOpen
|
||||||
|
? 'accordion__label accordion__label--open'
|
||||||
|
: 'accordion__label';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="accordion">
|
||||||
|
<div
|
||||||
|
className={accordionClass}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
>
|
||||||
|
{this.props.label}
|
||||||
|
</div>
|
||||||
|
{this.state.isOpen && (
|
||||||
|
<div className="accordion__content">
|
||||||
|
{this.props.children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Accordion.propTypes = {
|
||||||
|
children: PropTypes.node.isRequired,
|
||||||
|
label: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Accordion;
|
|
@ -157,3 +157,9 @@ export const UNSAFE_PORTS = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ALL_INTERFACES_IP = '0.0.0.0';
|
export const ALL_INTERFACES_IP = '0.0.0.0';
|
||||||
|
|
||||||
|
export const DHCP_STATUS_RESPONSE = {
|
||||||
|
YES: 'yes',
|
||||||
|
NO: 'no',
|
||||||
|
ERROR: 'error',
|
||||||
|
};
|
||||||
|
|
|
@ -292,18 +292,31 @@ const dhcp = handleActions({
|
||||||
|
|
||||||
[actions.findActiveDhcpRequest]: state => ({ ...state, processingStatus: true }),
|
[actions.findActiveDhcpRequest]: state => ({ ...state, processingStatus: true }),
|
||||||
[actions.findActiveDhcpFailure]: state => ({ ...state, processingStatus: false }),
|
[actions.findActiveDhcpFailure]: state => ({ ...state, processingStatus: false }),
|
||||||
[actions.findActiveDhcpSuccess]: (state, { payload }) => ({
|
[actions.findActiveDhcpSuccess]: (state, { payload }) => {
|
||||||
...state,
|
const {
|
||||||
active: payload,
|
other_server: otherServer,
|
||||||
processingStatus: false,
|
static_ip: staticIP,
|
||||||
}),
|
} = payload;
|
||||||
|
|
||||||
|
const newState = {
|
||||||
|
...state,
|
||||||
|
check: {
|
||||||
|
otherServer,
|
||||||
|
staticIP,
|
||||||
|
},
|
||||||
|
processingStatus: false,
|
||||||
|
};
|
||||||
|
return newState;
|
||||||
|
},
|
||||||
|
|
||||||
[actions.toggleDhcpRequest]: state => ({ ...state, processingDhcp: true }),
|
[actions.toggleDhcpRequest]: state => ({ ...state, processingDhcp: true }),
|
||||||
[actions.toggleDhcpFailure]: state => ({ ...state, processingDhcp: false }),
|
[actions.toggleDhcpFailure]: state => ({ ...state, processingDhcp: false }),
|
||||||
[actions.toggleDhcpSuccess]: (state) => {
|
[actions.toggleDhcpSuccess]: (state) => {
|
||||||
const { config } = state;
|
const { config } = state;
|
||||||
const newConfig = { ...config, enabled: !config.enabled };
|
const newConfig = { ...config, enabled: !config.enabled };
|
||||||
const newState = { ...state, config: newConfig, processingDhcp: false };
|
const newState = {
|
||||||
|
...state, config: newConfig, check: null, processingDhcp: false,
|
||||||
|
};
|
||||||
return newState;
|
return newState;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -324,7 +337,7 @@ const dhcp = handleActions({
|
||||||
config: {
|
config: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
},
|
},
|
||||||
active: null,
|
check: null,
|
||||||
leases: [],
|
leases: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,10 @@ var config = configuration{
|
||||||
{Filter: dnsfilter.Filter{ID: 3}, Enabled: false, URL: "https://hosts-file.net/ad_servers.txt", Name: "hpHosts - Ad and Tracking servers only"},
|
{Filter: dnsfilter.Filter{ID: 3}, Enabled: false, URL: "https://hosts-file.net/ad_servers.txt", Name: "hpHosts - Ad and Tracking servers only"},
|
||||||
{Filter: dnsfilter.Filter{ID: 4}, Enabled: false, URL: "https://www.malwaredomainlist.com/hostslist/hosts.txt", Name: "MalwareDomainList.com Hosts List"},
|
{Filter: dnsfilter.Filter{ID: 4}, Enabled: false, URL: "https://www.malwaredomainlist.com/hostslist/hosts.txt", Name: "MalwareDomainList.com Hosts List"},
|
||||||
},
|
},
|
||||||
|
DHCP: dhcpd.ServerConfig{
|
||||||
|
LeaseDuration: 86400,
|
||||||
|
ICMPTimeout: 1000,
|
||||||
|
},
|
||||||
SchemaVersion: currentSchemaVersion,
|
SchemaVersion: currentSchemaVersion,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
183
dhcp.go
183
dhcp.go
|
@ -2,14 +2,18 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/dhcpd"
|
"github.com/AdguardTeam/AdGuardHome/dhcpd"
|
||||||
|
"github.com/AdguardTeam/golibs/file"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/joomcode/errorx"
|
"github.com/joomcode/errorx"
|
||||||
)
|
)
|
||||||
|
@ -58,7 +62,17 @@ func handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if newconfig.Enabled {
|
if newconfig.Enabled {
|
||||||
err := dhcpServer.Start(&newconfig)
|
|
||||||
|
staticIP, err := hasStaticIP(newconfig.InterfaceName)
|
||||||
|
if !staticIP && err == nil {
|
||||||
|
err = setStaticIP(newconfig.InterfaceName)
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, http.StatusInternalServerError, "Failed to configure static IP: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = dhcpServer.Start(&newconfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, http.StatusBadRequest, "Failed to start DHCP server: %s", err)
|
httpError(w, http.StatusBadRequest, "Failed to start DHCP server: %s", err)
|
||||||
return
|
return
|
||||||
|
@ -130,6 +144,10 @@ func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Perform the following tasks:
|
||||||
|
// . Search for another DHCP server running
|
||||||
|
// . Check if a static IP is configured for the network interface
|
||||||
|
// Respond with results
|
||||||
func handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Request) {
|
func handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Tracef("%s %v", r.Method, r.URL)
|
log.Tracef("%s %v", r.Method, r.URL)
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
@ -147,13 +165,35 @@ func handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Error(w, errorText, http.StatusBadRequest)
|
http.Error(w, errorText, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
found, err := dhcpd.CheckIfOtherDHCPServersPresent(interfaceName)
|
found, err := dhcpd.CheckIfOtherDHCPServersPresent(interfaceName)
|
||||||
result := map[string]interface{}{}
|
|
||||||
if err != nil {
|
othSrv := map[string]interface{}{}
|
||||||
result["error"] = err.Error()
|
foundVal := "no"
|
||||||
} else {
|
if found {
|
||||||
result["found"] = found
|
foundVal = "yes"
|
||||||
|
} else if err != nil {
|
||||||
|
foundVal = "error"
|
||||||
|
othSrv["error"] = err.Error()
|
||||||
}
|
}
|
||||||
|
othSrv["found"] = foundVal
|
||||||
|
|
||||||
|
staticIP := map[string]interface{}{}
|
||||||
|
isStaticIP, err := hasStaticIP(interfaceName)
|
||||||
|
staticIPStatus := "yes"
|
||||||
|
if err != nil {
|
||||||
|
staticIPStatus = "error"
|
||||||
|
staticIP["error"] = err.Error()
|
||||||
|
} else if !isStaticIP {
|
||||||
|
staticIPStatus = "no"
|
||||||
|
staticIP["ip"] = getFullIP(interfaceName)
|
||||||
|
}
|
||||||
|
staticIP["static"] = staticIPStatus
|
||||||
|
|
||||||
|
result := map[string]interface{}{}
|
||||||
|
result["other_server"] = othSrv
|
||||||
|
result["static_ip"] = staticIP
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
err = json.NewEncoder(w).Encode(result)
|
err = json.NewEncoder(w).Encode(result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -162,6 +202,137 @@ func handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if network interface has a static IP configured
|
||||||
|
func hasStaticIP(ifaceName string) (bool, error) {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return false, errors.New("Can't detect static IP: not supported on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadFile("/etc/dhcpcd.conf")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(body), "\n")
|
||||||
|
nameLine := fmt.Sprintf("interface %s", ifaceName)
|
||||||
|
withinInterfaceCtx := false
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
|
if withinInterfaceCtx && len(line) == 0 {
|
||||||
|
// an empty line resets our state
|
||||||
|
withinInterfaceCtx = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(line) == 0 || line[0] == '#' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
|
if !withinInterfaceCtx {
|
||||||
|
if line == nameLine {
|
||||||
|
// we found our interface
|
||||||
|
withinInterfaceCtx = true
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if strings.HasPrefix(line, "interface ") {
|
||||||
|
// we found another interface - reset our state
|
||||||
|
withinInterfaceCtx = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(line, "static ip_address=") {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get IP address with netmask
|
||||||
|
func getFullIP(ifaceName string) string {
|
||||||
|
cmd := exec.Command("ip", "-oneline", "-family", "inet", "address", "show", ifaceName)
|
||||||
|
log.Tracef("executing %s %v", cmd.Path, cmd.Args)
|
||||||
|
d, err := cmd.Output()
|
||||||
|
if err != nil || cmd.ProcessState.ExitCode() != 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(string(d))
|
||||||
|
if len(fields) < 4 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
_, _, err = net.ParseCIDR(fields[3])
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gateway IP address
|
||||||
|
func getGatewayIP(ifaceName string) string {
|
||||||
|
cmd := exec.Command("ip", "route", "show", "dev", ifaceName)
|
||||||
|
log.Tracef("executing %s %v", cmd.Path, cmd.Args)
|
||||||
|
d, err := cmd.Output()
|
||||||
|
if err != nil || cmd.ProcessState.ExitCode() != 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(string(d))
|
||||||
|
if len(fields) < 3 || fields[0] != "default" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.ParseIP(fields[2])
|
||||||
|
if ip == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a static IP for network interface
|
||||||
|
func setStaticIP(ifaceName string) error {
|
||||||
|
ip := getFullIP(ifaceName)
|
||||||
|
if len(ip) == 0 {
|
||||||
|
return errors.New("Can't get IP address")
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadFile("/etc/dhcpcd.conf")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ip4, _, err := net.ParseCIDR(ip)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
add := fmt.Sprintf("\ninterface %s\nstatic ip_address=%s\n",
|
||||||
|
ifaceName, ip)
|
||||||
|
body = append(body, []byte(add)...)
|
||||||
|
|
||||||
|
gatewayIP := getGatewayIP(ifaceName)
|
||||||
|
if len(gatewayIP) != 0 {
|
||||||
|
add = fmt.Sprintf("static routers=%s\n",
|
||||||
|
gatewayIP)
|
||||||
|
body = append(body, []byte(add)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
add = fmt.Sprintf("static domain_name_servers=%s\n\n",
|
||||||
|
ip4)
|
||||||
|
body = append(body, []byte(add)...)
|
||||||
|
|
||||||
|
err = file.SafeWrite("/etc/dhcpcd.conf", body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func startDHCPServer() error {
|
func startDHCPServer() error {
|
||||||
if !config.DHCP.Enabled {
|
if !config.DHCP.Enabled {
|
||||||
// not enabled, don't do anything
|
// not enabled, don't do anything
|
||||||
|
|
Loading…
Reference in New Issue