Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZTP infrastructure changes to support DHCP discovery provisioning data #3298

Merged
merged 7 commits into from
Dec 10, 2019
5 changes: 1 addition & 4 deletions build_debian.sh
Original file line number Diff line number Diff line change
Expand Up @@ -387,9 +387,6 @@ set /files/etc/sysctl.conf/net.ipv6.conf.default.keep_addr_on_down 1
set /files/etc/sysctl.conf/net.ipv6.conf.all.keep_addr_on_down 1
set /files/etc/sysctl.conf/net.ipv6.conf.eth0.keep_addr_on_down 1

set /files/etc/sysctl.conf/net.ipv6.conf.eth0.accept_ra_defrtr 0
set /files/etc/sysctl.conf/net.ipv6.conf.eth0.accept_ra 0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i prefer to keep it as it is. if you need dhcp, then you can enable it. I saw you have this in you interfaces.j2 file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have it in /etc/sysctl.conf, there can be a case where the setting done by interface.j2 is overwritten when sysctl files are re-read by a different service. I moved setting of accept_ra to a new file "files/dhcp/90-dhcp6-systcl.conf.j2". This will decide whether accept_ra is 1/0. For DHCPv6 on eth0 to work, accept_ra must be 1.

The changes done as part of this PR enable DHCPv6 on eth0 by default just like how DHCP is enabled on eth0. This allows access to the switch using an IPv6 address. It has dual purpose to be used for ZTP over IPv6 as well as using it for management of SONiC switch using DHCPv6 assigned IPv6 address.

If we set accept_ra to 0 by default, the access to the switch using DHCPv6 is not possible.

Following are some options for us to consider:

  1. DHCPv6 is enabled when MGMT_INTERFACE table is not defined

This is part of the proposed change.

  1. DHCPv6 is enabled only when ZTP discovery is being performed.

This might momentarily work but user may lose connectivity to management interface if MGMT_INTRFACE configuration is not done or if accept_ra setting is not changed as part of ZTP configuration steps.

  1. DHCPv6 is enabled only when ZTP is enabled in the build

This can be confusing as certain build combinations allows IPv6 management access out of the box and not in others.


set /files/etc/sysctl.conf/net.ipv4.tcp_l3mdev_accept 1
set /files/etc/sysctl.conf/net.ipv4.udp_l3mdev_accept 1

Expand Down Expand Up @@ -424,10 +421,10 @@ EOF

sudo cp files/dhcp/rfc3442-classless-routes $FILESYSTEM_ROOT/etc/dhcp/dhclient-exit-hooks.d
sudo cp files/dhcp/sethostname $FILESYSTEM_ROOT/etc/dhcp/dhclient-exit-hooks.d/
sudo cp files/dhcp/sethostname6 $FILESYSTEM_ROOT/etc/dhcp/dhclient-exit-hooks.d/
sudo cp files/dhcp/graphserviceurl $FILESYSTEM_ROOT/etc/dhcp/dhclient-exit-hooks.d/
sudo cp files/dhcp/snmpcommunity $FILESYSTEM_ROOT/etc/dhcp/dhclient-exit-hooks.d/
sudo cp files/dhcp/vrf $FILESYSTEM_ROOT/etc/dhcp/dhclient-exit-hooks.d/
sudo cp files/dhcp/dhclient.conf $FILESYSTEM_ROOT/etc/dhcp/
if [ -f files/image_config/ntp/ntp ]; then
sudo cp ./files/image_config/ntp/ntp $FILESYSTEM_ROOT/etc/init.d/
fi
Expand Down
6 changes: 6 additions & 0 deletions files/build_templates/sonic_debian_extension.j2
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ sudo cp $IMAGE_CONFIGS/interfaces/interfaces-config.sh $FILESYSTEM_ROOT/usr/bin/
sudo cp $IMAGE_CONFIGS/interfaces/*.j2 $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
echo "interfaces-config.service" | sudo tee -a $GENERATED_SERVICE_FILE

# Copy dhcp client configuration template and create an initial configuration
sudo cp files/dhcp/dhclient.conf.j2 $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/
j2 files/dhcp/dhclient.conf.j2 | sudo tee $FILESYSTEM_ROOT/etc/dhcp/dhclient.conf
sudo cp files/dhcp/ifupdown2_policy.json $FILESYSTEM_ROOT/etc/network/ifupdown2/policy.d
sudo cp files/dhcp/90-dhcp6-systcl.conf.j2 $FILESYSTEM_ROOT_USR_SHARE_SONIC_TEMPLATES/

# Copy initial interfaces configuration file, will be overwritten on first boot
sudo cp $IMAGE_CONFIGS/interfaces/init_interfaces $FILESYSTEM_ROOT/etc/network/interfaces
sudo mkdir -p $FILESYSTEM_ROOT/etc/network/interfaces.d
Expand Down
7 changes: 7 additions & 0 deletions files/dhcp/90-dhcp6-systcl.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{% if MGMT_INTERFACE %}
net.ipv6.conf.eth0.accept_ra_defrtr = 0
net.ipv6.conf.eth0.accept_ra = 0
{% else %}
net.ipv6.conf.eth0.accept_ra_defrtr = 1
net.ipv6.conf.eth0.accept_ra = 1
{% endif %}
24 changes: 0 additions & 24 deletions files/dhcp/dhclient.conf

This file was deleted.

45 changes: 45 additions & 0 deletions files/dhcp/dhclient.conf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{% block banner %}
# =============== Managed by SONiC Config Engine DO NOT EDIT! ===============
# generated from /usr/share/sonic/templates/dhclient.conf.j2 using sonic-cfggen
# file: /etc/dhcp/dhclient.conf
#
{% endblock banner %}
# Configuration file for /sbin/dhclient, which is included in Debian's
# dhcp3-client package.
#
# This is a sample configuration file for dhclient. See dhclient.conf's
# man page for more information about the syntax of this file
# and a more comprehensive list of the parameters understood by
# dhclient.
#
# Normally, if the DHCP server provides reasonable information and does
# not leave anything out (like the domain name, for example), then
# few changes must be made to this file, if any.
#

option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
option snmp-community code 224 = text;
option minigraph-url code 225 = text;
option acl-url code 226 = text;
option tftp-server-name code 66 = text;
option bootfile-name code 67 = text;
option user-class code 77 = text;
option provisioning-script-url code 239 = text;
option dhcp6.user-class code 15 = text;
option dhcp6.provisioning-script-url code 239 = text;
option dhcp6.boot-file-url code 59 = text;

send host-name = gethostname();
request subnet-mask, broadcast-address, time-offset, routers,
domain-name, domain-name-servers, domain-search, host-name,
dhcp6.name-servers, dhcp6.domain-search, interface-mtu, dhcp6.fqdn,
rfc3442-classless-static-routes, ntp-servers, log-servers,
{%- if ZTP is defined and ZTP_DHCP_DISABLED is not defined -%}bootfile-name, provisioning-script-url, tftp-server-name,
dhcp6.provisioning-script-url, dhcp6.boot-file-url,{%- endif -%}
snmp-community, minigraph-url, acl-url;
{% if ZTP is defined and ZTP_DHCP_DISABLED is not defined %}
send user-class "SONiC-ZTP";
send dhcp6.user-class "SONiC-ZTP";
send dhcp-client-identifier "SONiC##{{ ZTP['mode']['product-name'] }}##{{ ZTP['mode']['serial-no'] }}";
retry 60;
{% endif %}
26 changes: 14 additions & 12 deletions files/dhcp/graphserviceurl
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
case $reason in
BOUND|RENEW|REBIND|REBOOT)
if [ -n "$new_minigraph_url" ]; then
echo $new_minigraph_url > /tmp/dhcp_graph_url
else
echo "N/A" > /tmp/dhcp_graph_url
fi
if [ -n "$new_acl_url" ]; then
echo $new_acl_url > /tmp/dhcp_acl_url
fi
;;
esac
if [ ! -e /usr/bin/ztp ] || [ "$(ztp status -c)" = "0:DISABLED" ]; then
case $reason in
BOUND|RENEW|REBIND|REBOOT)
if [ -n "$new_minigraph_url" ]; then
echo $new_minigraph_url > /tmp/dhcp_graph_url
else
echo "N/A" > /tmp/dhcp_graph_url
fi
if [ -n "$new_acl_url" ]; then
echo $new_acl_url > /tmp/dhcp_acl_url
fi
;;
esac
fi
12 changes: 12 additions & 0 deletions files/dhcp/ifupdown2_policy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"dhcp" : {
"defaults" : {
"dhcp-wait" : "no"
},
"iface_defaults" : {
"eth0" : {
"dhcp6-duid" : "LL"
}
}
}
}
7 changes: 6 additions & 1 deletion files/dhcp/rfc3442-classless-routes
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,13 @@ if [ "$RUN" = "yes" ]; then
fi

# set route (ip detects host routes automatically)
ip -4 route add "${net_address}/${net_length}" \
if echo $interface | grep -v Ethernet ; then
ip -4 route add "${net_address}/${net_length}" \
${via_arg} dev "${interface}" table default >/dev/null 2>&1
else
ip -4 route add "${net_address}/${net_length}" \
${via_arg} dev "${interface}" >/dev/null 2>&1
fi
done
fi
fi
Expand Down
14 changes: 14 additions & 0 deletions files/dhcp/sethostname6
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
case $reason in
BOUND6|RENEW6|REBIND6|REBOOT)
current_dhcp6_fqdn=`hostname`
if [ "$current_dhcp6_fqdn" != "$new_dhcp6_fqdn" ] && [ -n "$new_dhcp6_fqdn" ]
then
echo $new_dhcp6_fqdn > /etc/hostname
hostname -F /etc/hostname
sed -i "/\s$current_dhcp6_fqdn$/d" /etc/hosts
sed -i "/\s$new_dhcp6_fqdn$/d" /etc/hosts
echo "127.0.0.1 $new_dhcp6_fqdn" >> /etc/hosts
echo ":: $new_dhcp6_fqdn" >> /etc/hosts
fi
;;
esac
32 changes: 31 additions & 1 deletion files/image_config/interfaces/interfaces-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,40 @@

ifdown --force eth0

sonic-cfggen -d -t /usr/share/sonic/templates/interfaces.j2 > /etc/network/interfaces
# Check if ZTP DHCP policy has been installed
if [ -e /etc/network/ifupdown2/policy.d/ztp_dhcp.json ]; then
# Obtain port operational state information
redis-dump -d 0 -k "PORT_TABLE:Ethernet*" -y > /tmp/ztp_port_data.json

if [ $? -ne 0 ] || [ ! -e /tmp/ztp_port_data.json ] || [ "$(cat /tmp/ztp_port_data.json)" = "" ]; then
echo "{}" > /tmp/ztp_port_data.json
fi

# Create an input file with ztp input information
echo "{ \"PORT_DATA\" : $(cat /tmp/ztp_port_data.json) }" > \
/tmp/ztp_input.json
else
echo "{ \"ZTP_DHCP_DISABLED\" : \"true\" }" > /tmp/ztp_input.json
fi

# Create /e/n/i file for existing and active interfaces
sonic-cfggen -d -j /tmp/ztp_input.json -t /usr/share/sonic/templates/interfaces.j2 > /etc/network/interfaces

[ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pid
[ -f /var/run/dhclient6.eth0.pid ] && kill `cat /var/run/dhclient6.eth0.pid` && rm -f /var/run/dhclient6.eth0.pid

for intf_pid in $(ls -1 /var/run/dhclient*.Ethernet*.pid 2> /dev/null); do
[ -f ${intf_pid} ] && kill `cat ${intf_pid}` && rm -f ${intf_pid}
done

sonic-cfggen -d -j /tmp/ztp_input.json -t /usr/share/sonic/templates/90-dhcp6-systcl.conf.j2 > /etc/sysctl.d/90-dhcp6-systcl.conf
# Read sysctl conf files again
sysctl -p /etc/sysctl.d/90-dhcp6-systcl.conf

sonic-cfggen -d -j /tmp/ztp_input.json -t /usr/share/sonic/templates/dhclient.conf.j2 > /etc/dhcp/dhclient.conf
systemctl restart networking

# Clean-up created files
rm -f /tmp/ztp_input.json /tmp/ztp_port_data.json

ifdown lo && ifup lo
39 changes: 39 additions & 0 deletions files/image_config/interfaces/interfaces.j2
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,41 @@ iface lo {{ 'inet' if prefix | ipv4 else 'inet6' }} static

# The management network interface
auto eth0
{% if (ZTP_DHCP_DISABLED is not defined) and (ZTP is defined) and (ZTP['mode'] is defined and ZTP['mode']['profile'] == 'active') %}


# ZTP out-of-band interface
allow-hotplug eth0
{% if ZTP['mode']['ipv4'] == 'true' %}
iface eth0 inet dhcp
{% endif %}
{% if ZTP['mode']['ipv6'] == 'true' %}
iface eth0 inet6 dhcp
up sysctl net.ipv6.conf.eth0.accept_ra=1
down sysctl net.ipv6.conf.eth0.accept_ra=0
{% endif %}

{% if ZTP['mode']['inband'] == 'true' %}
{% for port in PORT %}
{% set var = {'port_exists': True if port | is_intf_exists else False } %}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you are doing inband, when do you care whether the interface exists or not. by default all interface should be created by swss. in line 58, you have hotplug meaning you can wait for the interface to appear and then start doing dhcp.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed and I will remove the interface exists check.

Also, the hotplug even though mentioned in the configuration, the feature is not fully functional in ifupdown2. I checked with the ifupdown2 maintainer and it is on the ToDo list.

{% if var.port_exists %}

# ZTP in-band interface {{ port }}
auto {{ port }}
allow-hotplug {{ port }}
{% if PORT_DATA['PORT_TABLE:'+port] is defined and PORT_DATA['PORT_TABLE:'+port]['value']['oper_status'] == 'up' %}
{% if ZTP['mode']['ipv4'] == 'true' %}
iface {{ port }} inet dhcp
{% endif %}
{% if ZTP['mode']['ipv6'] == 'true' %}
iface {{ port }} inet6 dhcp
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}

{% else %}
{% if MGMT_INTERFACE %}
{% for (name, prefix) in MGMT_INTERFACE|pfx_filter %}
iface eth0 {{ 'inet' if prefix | ipv4 else 'inet6' }} static
Expand Down Expand Up @@ -77,6 +112,10 @@ iface eth0 inet dhcp
up cgset -r l3mdev.master-device=mgmt mgmt
down cgdelete -g l3mdev:mgmt
{% endif %}
iface eth0 inet6 dhcp
up sysctl net.ipv6.conf.eth0.accept_ra=1
down sysctl net.ipv6.conf.eth0.accept_ra=0
{% endif %}
{% endif %}
#
source /etc/network/interfaces.d/*
Expand Down
9 changes: 9 additions & 0 deletions src/sonic-config-engine/sonic-cfggen
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ def pfx_filter(value):
table[key] = val
return table

def is_intf_exists(value):
if not value:
return False
link_state = '/sys/class/net/'+value+'/operstate'
if os.path.isfile(link_state):
return True
return False

def ip_network(value):
""" Extract network for network prefix """
try:
Expand Down Expand Up @@ -281,6 +289,7 @@ def main():
redis_bcc = RedisBytecodeCache(SonicV2Connector(host='127.0.0.1'))
env = jinja2.Environment(loader=loader, trim_blocks=True, bytecode_cache=redis_bcc)
env.filters['sort_by_port_index'] = sort_by_port_index
env.filters['is_intf_exists'] = is_intf_exists
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config engine should not take current system state, it only parses the config db or minigraph. otherwise, it is making sonic-cfggen complicated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed and I will remove the newly added filter. It is not required as well.

env.filters['ipv4'] = is_ipv4
env.filters['ipv6'] = is_ipv6
env.filters['unique_name'] = unique_name
Expand Down