From 482f0d3c0b8ed68ce54745645bf5f90c7035b54b Mon Sep 17 00:00:00 2001 From: kellyyeh <42761586+kellyyeh@users.noreply.github.com> Date: Wed, 29 Sep 2021 18:20:39 -0700 Subject: [PATCH 1/7] Add DHCPv6 minigraph parsing support (#8870) --- src/sonic-config-engine/minigraph.py | 13 +++++++++++-- src/sonic-config-engine/tests/t0-sample-graph.xml | 1 + 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index 67356d0b1c5a..983da9de7d0e 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -385,6 +385,12 @@ def parse_dpg(dpg, hname): vdhcpserver_list = vintfdhcpservers.split(';') vlan_attributes['dhcp_servers'] = vdhcpserver_list + vintf_node = vintf.find(str(QName(ns, "Dhcpv6Relays"))) + if vintf_node is not None and vintf_node.text is not None: + vintfdhcpservers = vintf_node.text + vdhcpserver_list = vintfdhcpservers.split(';') + vlan_attributes['dhcpv6_servers'] = vdhcpserver_list + sonic_vlan_name = "Vlan%s" % vlanid if sonic_vlan_name != vintfname: vlan_attributes['alias'] = vintfname @@ -606,6 +612,7 @@ def parse_cpg(cpg, hname, local_devices=[]): def parse_meta(meta, hname): syslog_servers = [] dhcp_servers = [] + dhcpv6_servers = [] ntp_servers = [] tacacs_servers = [] mgmt_routes = [] @@ -648,7 +655,7 @@ def parse_meta(meta, hname): qos_profile = value elif name == "ResourceType": resource_type = value - return syslog_servers, dhcp_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, downstream_subrole, qos_profile, resource_type + return syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, downstream_subrole, qos_profile, resource_type def parse_linkmeta(meta, hname): @@ -935,6 +942,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None): console_ports = {} syslog_servers = [] dhcp_servers = [] + dhcpv6_servers = [] ntp_servers = [] tacacs_servers = [] mgmt_routes = [] @@ -985,7 +993,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None): elif child.tag == str(QName(ns, "UngDec")): (u_neighbors, u_devices, _, _, _, _, _, _) = parse_png(child, hostname) elif child.tag == str(QName(ns, "MetadataDeclaration")): - (syslog_servers, dhcp_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, downstream_subrole, qos_profile, resource_type) = parse_meta(child, hostname) + (syslog_servers, dhcp_servers, dhcppv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, downstream_subrole, qos_profile, resource_type) = parse_meta(child, hostname) elif child.tag == str(QName(ns, "LinkMetadataDeclaration")): linkmetas = parse_linkmeta(child, hostname) elif child.tag == str(QName(ns, "DeviceInfos")): @@ -1245,6 +1253,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None): results['DEVICE_NEIGHBOR_METADATA'] = { key:devices[key] for key in devices if key in {device['name'] for device in neighbors.values()} } results['SYSLOG_SERVER'] = dict((item, {}) for item in syslog_servers) results['DHCP_SERVER'] = dict((item, {}) for item in dhcp_servers) + results['DHCPv6_SERVER'] = dict((item, {}) for item in dhcpv6_servers) results['NTP_SERVER'] = dict((item, {}) for item in ntp_servers) results['TACPLUS_SERVER'] = dict((item, {'priority': '1', 'tcp_port': '49'}) for item in tacacs_servers) results['ACL_TABLE'] = filter_acl_table_bindings(acls, neighbors, pcs, sub_role) diff --git a/src/sonic-config-engine/tests/t0-sample-graph.xml b/src/sonic-config-engine/tests/t0-sample-graph.xml index f847af1f90cf..4c584cd4c7c2 100644 --- a/src/sonic-config-engine/tests/t0-sample-graph.xml +++ b/src/sonic-config-engine/tests/t0-sample-graph.xml @@ -240,6 +240,7 @@ 192.0.0.1;192.0.0.2 + fc02:2000::1;fc02:2000::2 1000 1000 192.168.0.0/27 From 35b5578d58212d49511f2c95b15b842351e28af5 Mon Sep 17 00:00:00 2001 From: kellyyeh Date: Tue, 6 Sep 2022 23:39:56 +0000 Subject: [PATCH 2/7] Parse DHCP Table (#8988) --- src/sonic-config-engine/minigraph.py | 30 +++++++++++++++++-- .../tests/simple-sample-graph-case.xml | 12 ++++++++ src/sonic-config-engine/tests/test_cfggen.py | 8 +++++ .../tests/test_minigraph_case.py | 24 +++++++++++++++ 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index 983da9de7d0e..836149bcbfd0 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -396,6 +396,29 @@ def parse_dpg(dpg, hname): vlan_attributes['alias'] = vintfname vlans[sonic_vlan_name] = vlan_attributes + dhcp = child.find(str(QName(ns, "Dhcp"))) + dhcp_table = {} + + if dhcp is not None: + for vintf in dhcp.findall(str(QName(ns, "VlanInterface"))): + vintfname = vintf.find(str(QName(ns, "Name"))).text + + dhcp_attributes = {} + + dhcp_node = vintf.find(str(QName(ns, "Dhcpv6Relays"))) + if dhcp_node is not None and dhcp_node.text is not None: + dhcpservers = dhcp_node.text + vdhcpserver_list = dhcpservers.split(';') + dhcp_attributes['dhcpv6_servers'] = vdhcpserver_list + + option_linklayer_addr = vintf.find(str(QName(ns, "Dhcpv6OptionRfc6939"))) + if option_linklayer_addr is not None and option_linklayer_addr.text == "true": + dhcp_attributes['dhcpv6_option|rfc6939_support'] = "true" + elif option_linklayer_addr is not None and option_linklayer_addr.text == "false": + dhcp_attributes['dhcpv6_option|rfc6939_support'] = "false" + + dhcp_table[vintfname] = dhcp_attributes + acls = {} for aclintf in aclintfs.findall(str(QName(ns, "AclInterface"))): if aclintf.find(str(QName(ns, "InAcl"))) is not None: @@ -496,7 +519,7 @@ def parse_dpg(dpg, hname): except: print >> sys.stderr, "Warning: Ignoring Control Plane ACL %s without type" % aclname - return intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni + return intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, dhcp_table, pcs, pc_members, acls, vni return None, None, None, None, None, None, None, None, None, None def parse_host_loopback(dpg, hname): @@ -985,7 +1008,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None): for child in root: if asic_name is None: if child.tag == str(QName(ns, "DpgDec")): - (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni) = parse_dpg(child, hostname) + (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, dhcp_table, pcs, pc_members, acls, vni) = parse_dpg(child, hostname) elif child.tag == str(QName(ns, "CpgDec")): (bgp_sessions, bgp_internal_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, hostname) elif child.tag == str(QName(ns, "PngDec")): @@ -1000,7 +1023,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None): (port_speeds_default, port_descriptions) = parse_deviceinfo(child, hwsku) else: if child.tag == str(QName(ns, "DpgDec")): - (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, pcs, pc_members, acls, vni) = parse_dpg(child, asic_name) + (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, dhcp_table, pcs, pc_members, acls, vni) = parse_dpg(child, asic_name) host_lo_intfs = parse_host_loopback(child, hostname) elif child.tag == str(QName(ns, "CpgDec")): (bgp_sessions, bgp_internal_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, asic_name, local_devices) @@ -1254,6 +1277,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None): results['SYSLOG_SERVER'] = dict((item, {}) for item in syslog_servers) results['DHCP_SERVER'] = dict((item, {}) for item in dhcp_servers) results['DHCPv6_SERVER'] = dict((item, {}) for item in dhcpv6_servers) + results['DHCP'] = dhcp_table results['NTP_SERVER'] = dict((item, {}) for item in ntp_servers) results['TACPLUS_SERVER'] = dict((item, {'priority': '1', 'tcp_port': '49'}) for item in tacacs_servers) results['ACL_TABLE'] = filter_acl_table_bindings(acls, neighbors, pcs, sub_role) diff --git a/src/sonic-config-engine/tests/simple-sample-graph-case.xml b/src/sonic-config-engine/tests/simple-sample-graph-case.xml index add1964c5243..48460e2c6da8 100644 --- a/src/sonic-config-engine/tests/simple-sample-graph-case.xml +++ b/src/sonic-config-engine/tests/simple-sample-graph-case.xml @@ -136,6 +136,18 @@ 192.168.0.0/27 + + + Vlan1000 + fc02:2000::1;fc02:2000::2 + true + + + Vlan2000 + fc02:2000::3;fc02:2000::4 + false + + diff --git a/src/sonic-config-engine/tests/test_cfggen.py b/src/sonic-config-engine/tests/test_cfggen.py index da8c88fd7042..4782c7fc2814 100644 --- a/src/sonic-config-engine/tests/test_cfggen.py +++ b/src/sonic-config-engine/tests/test_cfggen.py @@ -617,3 +617,11 @@ def test_show_run_interfaces(self): argument = '-a \'{"key1":"value"}\' --var-json INTERFACE' output = self.run_script(argument) self.assertEqual(output, '') + + def test_minigraph_dhcp(self): + argument = '-m "' + self.sample_graph_simple_case + '" -p "' + self.port_config + '" -v DHCP' + output = self.run_script(argument) + self.assertEqual( + output.strip(), + "{'Vlan1000': {'dhcpv6_option|rfc6939_support': 'true', 'dhcpv6_servers': ['fc02:2000::1', 'fc02:2000::2']}, 'Vlan2000': {'dhcpv6_option|rfc6939_support': 'false', 'dhcpv6_servers': ['fc02:2000::3', 'fc02:2000::4']}}" + ) \ No newline at end of file diff --git a/src/sonic-config-engine/tests/test_minigraph_case.py b/src/sonic-config-engine/tests/test_minigraph_case.py index 3d0552fc235c..85114396fce7 100644 --- a/src/sonic-config-engine/tests/test_minigraph_case.py +++ b/src/sonic-config-engine/tests/test_minigraph_case.py @@ -190,3 +190,27 @@ def test_parse_device_desc_xml_mgmt_interface(self): self.assertEqual(len(mgmt_intf.keys()), 1) self.assertTrue(('eth0', 'FC00:1::32/64') in mgmt_intf.keys()) self.assertTrue(ipaddress.IPAddress('fc00:1::1') == mgmt_intf[('eth0', 'FC00:1::32/64')]['gwaddr']) + + def test_dhcp_table(self): + argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v "DHCP"' + expected = { + 'Vlan1000': { + 'dhcpv6_servers': [ + "fc02:2000::1", + "fc02:2000::2" + ], + 'dhcpv6_option|rfc6939_support': 'true' + }, + 'Vlan2000': { + 'dhcpv6_servers': [ + "fc02:2000::3", + "fc02:2000::4" + ], + 'dhcpv6_option|rfc6939_support': 'false' + } + } + output = self.run_script(argument) + self.assertEqual( + output.strip(), + "{'Vlan1000': {'dhcpv6_option|rfc6939_support': 'true', 'dhcpv6_servers': ['fc02:2000::1', 'fc02:2000::2']}, 'Vlan2000': {'dhcpv6_option|rfc6939_support': 'false', 'dhcpv6_servers': ['fc02:2000::3', 'fc02:2000::4']}}" + ) \ No newline at end of file From 01a8af14aa94d2f61aef80cb6fefbe1f3307c9ed Mon Sep 17 00:00:00 2001 From: kellyyeh Date: Tue, 13 Sep 2022 00:35:12 +0000 Subject: [PATCH 3/7] Replace isc-dhcp with DHCPv6 Relay in dhcp_relay docker (#8884) --- dockers/docker-dhcp-relay/Dockerfile.j2 | 1 + dockers/docker-dhcp-relay/critical_processes | 2 +- .../docker-dhcp-relay/dhcp-relay.monitors.j2 | 76 ++ .../docker-dhcp-relay/dhcp-relay.programs.j2 | 22 + .../docker-dhcp-relay/dhcpv4-relay.agents.j2 | 41 + .../docker-dhcp-relay/dhcpv6-relay.agents.j2 | 23 + .../docker-dhcp-relay.supervisord.conf.j2 | 119 +-- dockers/docker-dhcp-relay/start.sh | 2 +- dockers/docker-dhcp-relay/wait_for_intf.sh.j2 | 5 + rules/dhcp6relay.mk | 12 + rules/docker-dhcp-relay.mk | 4 +- sonic-dhcp-relay | 1 + src/dhcp6relay/.gitignore | 5 + src/dhcp6relay/Makefile | 42 + src/dhcp6relay/debian/changelog | 5 + src/dhcp6relay/debian/compat | 1 + src/dhcp6relay/debian/control | 27 + src/dhcp6relay/debian/rules | 12 + src/dhcp6relay/src/configInterface.cpp | 151 ++++ src/dhcp6relay/src/configInterface.d | 44 + src/dhcp6relay/src/configInterface.h | 75 ++ src/dhcp6relay/src/main.cpp | 17 + src/dhcp6relay/src/main.d | 44 + src/dhcp6relay/src/relay.cpp | 816 ++++++++++++++++++ src/dhcp6relay/src/relay.d | 51 ++ src/dhcp6relay/src/relay.h | 349 ++++++++ src/dhcp6relay/src/subdir.mk | 23 + src/sonic-config-engine/minigraph.py | 18 +- .../docker-dhcp-relay.supervisord.conf | 19 +- .../tests/sample_output/wait_for_intf.sh | 4 + .../tests/simple-sample-graph-case.xml | 18 +- src/sonic-config-engine/tests/test_cfggen.py | 4 +- .../tests/test_minigraph_case.py | 60 +- src/sonic-dhcp-relay | 1 + 34 files changed, 1941 insertions(+), 153 deletions(-) create mode 100644 dockers/docker-dhcp-relay/dhcp-relay.monitors.j2 create mode 100644 dockers/docker-dhcp-relay/dhcp-relay.programs.j2 create mode 100644 dockers/docker-dhcp-relay/dhcpv4-relay.agents.j2 create mode 100644 dockers/docker-dhcp-relay/dhcpv6-relay.agents.j2 create mode 100644 rules/dhcp6relay.mk create mode 160000 sonic-dhcp-relay create mode 100644 src/dhcp6relay/.gitignore create mode 100644 src/dhcp6relay/Makefile create mode 100644 src/dhcp6relay/debian/changelog create mode 100644 src/dhcp6relay/debian/compat create mode 100644 src/dhcp6relay/debian/control create mode 100755 src/dhcp6relay/debian/rules create mode 100644 src/dhcp6relay/src/configInterface.cpp create mode 100644 src/dhcp6relay/src/configInterface.d create mode 100644 src/dhcp6relay/src/configInterface.h create mode 100644 src/dhcp6relay/src/main.cpp create mode 100644 src/dhcp6relay/src/main.d create mode 100644 src/dhcp6relay/src/relay.cpp create mode 100644 src/dhcp6relay/src/relay.d create mode 100644 src/dhcp6relay/src/relay.h create mode 100644 src/dhcp6relay/src/subdir.mk create mode 160000 src/sonic-dhcp-relay diff --git a/dockers/docker-dhcp-relay/Dockerfile.j2 b/dockers/docker-dhcp-relay/Dockerfile.j2 index e909c6a70040..22662fbb7f73 100644 --- a/dockers/docker-dhcp-relay/Dockerfile.j2 +++ b/dockers/docker-dhcp-relay/Dockerfile.j2 @@ -26,6 +26,7 @@ RUN apt-get clean -y && \ COPY ["docker_init.sh", "start.sh", "/usr/bin/"] COPY ["docker-dhcp-relay.supervisord.conf.j2", "port-name-alias-map.txt.j2", "wait_for_intf.sh.j2", "/usr/share/sonic/templates/"] +COPY ["dhcp-relay.programs.j2", "dhcpv4-relay.agents.j2", "dhcpv6-relay.agents.j2", "dhcp-relay.monitors.j2", "/usr/share/sonic/templates/"] COPY ["files/supervisor-proc-exit-listener", "/usr/bin"] COPY ["critical_processes", "/etc/supervisor"] diff --git a/dockers/docker-dhcp-relay/critical_processes b/dockers/docker-dhcp-relay/critical_processes index 855851bf2d68..43bf6af2794f 100644 --- a/dockers/docker-dhcp-relay/critical_processes +++ b/dockers/docker-dhcp-relay/critical_processes @@ -1 +1 @@ -group:isc-dhcp-relay +group:dhcp-relay diff --git a/dockers/docker-dhcp-relay/dhcp-relay.monitors.j2 b/dockers/docker-dhcp-relay/dhcp-relay.monitors.j2 new file mode 100644 index 000000000000..0928bac22d4d --- /dev/null +++ b/dockers/docker-dhcp-relay/dhcp-relay.monitors.j2 @@ -0,0 +1,76 @@ +[group:dhcpmon] +programs= +{%- set add_preceding_comma = { 'flag': False } %} +{% set monitor_instance = { 'flag': False } %} +{% for vlan_name in VLAN_INTERFACE %} +{% if VLAN and vlan_name in VLAN and 'dhcp_servers' in VLAN[vlan_name] and VLAN[vlan_name]['dhcp_servers']|length > 0 %} +{% set _dummy = monitor_instance.update({'flag': True}) %} +{%- endif %} +{% if DHCP_RELAY and vlan_name in DHCP_RELAY and DHCP_RELAY[vlan_name]['dhcpv6_servers']|length > 0 %} +{% set _dummy = monitor_instance.update({'flag': True}) %} +{%- endif %} +{% if monitor_instance.flag %} +{% if add_preceding_comma.flag %},{% endif %} +{% set _dummy = add_preceding_comma.update({'flag': True}) %} +dhcpmon-{{ vlan_name }} +{%- set _dummy = monitor_instance.update({'flag': False}) %} +{%- endif %} +{% endfor %} + + +{# Create a program entry for each DHCP MONitor instance #} +{% set relay_for_ipv4 = { 'flag': False } %} +{% set relay_for_ipv6 = { 'flag': False } %} +{% for vlan_name in VLAN_INTERFACE %} +{# Check DHCPv4 agents #} +{% if VLAN and vlan_name in VLAN and 'dhcp_servers' in VLAN[vlan_name] and VLAN[vlan_name]['dhcp_servers']|length > 0 %} +{% for dhcp_server in VLAN[vlan_name]['dhcp_servers'] %} +{% if dhcp_server | ipv4 %} +{% set _dummy = relay_for_ipv4.update({'flag': True}) %} +{% endif %} +{% endfor %} +{% endif %} +{# Check DHCPv6 agents #} +{% if DHCP_RELAY and vlan_name in DHCP_RELAY and DHCP_RELAY[vlan_name]['dhcpv6_servers']|length > 0 %} +{% for dhcpv6_server in VLAN[vlan_name]['dhcpv6_servers'] %} +{% if dhcpv6_server | ipv6 %} +{% set _dummy = relay_for_ipv6.update({'flag': True}) %} +{% endif %} +{% endfor %} +{% endif %} +{% if relay_for_ipv4.flag or relay_for_ipv6.flag %} +[program:dhcpmon-{{ vlan_name }}] +{# We treat this VLAN as a downstream interface (-id), as we only want to listen for requests #} +command=/usr/sbin/dhcpmon -id {{ vlan_name }} +{#- Dual ToR Option #} +{% if 'subtype' in DEVICE_METADATA['localhost'] and DEVICE_METADATA['localhost']['subtype'] == 'DualToR' %} -u Loopback0{% endif -%} +{#- We treat all other interfaces as upstream interfaces (-iu), as we only want to listen for replies #} +{% for (name, prefix) in VLAN_INTERFACE|pfx_filter %} +{% if prefix | ipv4 and name != vlan_name %} -iu {{ name }}{% endif -%} +{% endfor %} +{% for (name, prefix) in INTERFACE|pfx_filter %} +{% if prefix | ipv4 %} -iu {{ name }}{% endif -%} +{% endfor %} +{% for (name, prefix) in PORTCHANNEL_INTERFACE|pfx_filter %} +{% if prefix | ipv4 %} -iu {{ name }}{% endif -%} +{% endfor %} +{% if MGMT_INTERFACE %} +{% for (name, prefix) in MGMT_INTERFACE|pfx_filter %} +{% if prefix | ipv4 %} -im {{ name }}{% endif -%} +{% endfor %} +{% endif %} + +priority=4 +autostart=false +autorestart=false +stdout_logfile=syslog +stderr_logfile=syslog +dependent_startup=true +dependent_startup_wait_for= +{%- if relay_for_ipv4.flag %}isc-dhcpv4-relay-{{ vlan_name }}:running {% endif %} + + +{% set _dummy = relay_for_ipv4.update({'flag': False}) %} +{% set _dummy = relay_for_ipv6.update({'flag': False}) %} +{% endif %} +{% endfor %} diff --git a/dockers/docker-dhcp-relay/dhcp-relay.programs.j2 b/dockers/docker-dhcp-relay/dhcp-relay.programs.j2 new file mode 100644 index 000000000000..9d5b62c6b236 --- /dev/null +++ b/dockers/docker-dhcp-relay/dhcp-relay.programs.j2 @@ -0,0 +1,22 @@ +[group:dhcp-relay] +programs= +{%- set relay_for_ipv6 = { 'flag': False } %} +{%- set add_preceding_comma = { 'flag': False } %} +{% for vlan_name in VLAN_INTERFACE %} +{# Append DHCPv4 agents #} +{% if VLAN and vlan_name in VLAN and 'dhcp_servers' in VLAN[vlan_name] and VLAN[vlan_name]['dhcp_servers']|length > 0 %} +{% if add_preceding_comma.flag %},{% endif %} +{% set _dummy = add_preceding_comma.update({'flag': True}) %} +isc-dhcpv4-relay-{{ vlan_name }} +{%- endif %} +{% if DHCP_RELAY and vlan_name in DHCP_RELAY and DHCP_RELAY[vlan_name]['dhcpv6_servers']|length > 0 %} +{% set _dummy = relay_for_ipv6.update({'flag': True}) %} +{%- endif %} +{% endfor %} +{# Append DHCPv6 agents #} +{% if relay_for_ipv6.flag %} +{% if add_preceding_comma.flag %},{% endif %} +{% set _dummy = add_preceding_comma.update({'flag': True}) %} +dhcp6relay +{% endif %} + diff --git a/dockers/docker-dhcp-relay/dhcpv4-relay.agents.j2 b/dockers/docker-dhcp-relay/dhcpv4-relay.agents.j2 new file mode 100644 index 000000000000..499afed54ef9 --- /dev/null +++ b/dockers/docker-dhcp-relay/dhcpv4-relay.agents.j2 @@ -0,0 +1,41 @@ +{# Append DHCPv4 agents #} +{% if VLAN and vlan_name in VLAN and 'dhcp_servers' in VLAN[vlan_name] and VLAN[vlan_name]['dhcp_servers']|length > 0 %} +{% for dhcp_server in VLAN[vlan_name]['dhcp_servers'] %} +{% if dhcp_server | ipv4 %} +{% set _dummy = relay_for_ipv4.update({'flag': True}) %} +{% endif %} +{% endfor %} +{% if relay_for_ipv4.flag %} +{% set _dummy = relay_for_ipv4.update({'flag': False}) %} + +[program:isc-dhcpv4-relay-{{ vlan_name }}] +{# We treat this VLAN as a downstream interface (-id), as we only want to listen for requests #} +command=/usr/sbin/dhcrelay -d -m discard -a %%h:%%p %%P --name-alias-map-file /tmp/port-name-alias-map.txt -id {{ vlan_name }} +{#- Dual ToR Option #} +{% if 'subtype' in DEVICE_METADATA['localhost'] and DEVICE_METADATA['localhost']['subtype'] == 'DualToR' %} -U Loopback0 -dt{% endif -%} +{#- si option to use intf addr in relay #} +{% if DEVICE_METADATA['localhost']['deployment_id'] == '8' %} -si{% endif -%} +{#- We treat all other interfaces as upstream interfaces (-iu), as we only want to listen for replies #} +{% for (name, prefix) in VLAN_INTERFACE|pfx_filter %} +{% if prefix | ipv4 and name != vlan_name %} -iu {{ name }}{% endif -%} +{% endfor %} +{% for (name, prefix) in INTERFACE|pfx_filter %} +{% if prefix | ipv4 %} -iu {{ name }}{% endif -%} +{% endfor %} +{% for (name, prefix) in PORTCHANNEL_INTERFACE|pfx_filter %} +{% if prefix | ipv4 %} -iu {{ name }}{% endif -%} +{% endfor %} +{% for dhcp_server in VLAN[vlan_name]['dhcp_servers'] %} +{%- if dhcp_server | ipv4 %} {{ dhcp_server }}{% endif -%} +{% endfor %} + +priority=3 +autostart=false +autorestart=false +stdout_logfile=syslog +stderr_logfile=syslog +dependent_startup=true +dependent_startup_wait_for=start:exited + +{% endif %} +{% endif %} diff --git a/dockers/docker-dhcp-relay/dhcpv6-relay.agents.j2 b/dockers/docker-dhcp-relay/dhcpv6-relay.agents.j2 new file mode 100644 index 000000000000..3b14d11e3bcd --- /dev/null +++ b/dockers/docker-dhcp-relay/dhcpv6-relay.agents.j2 @@ -0,0 +1,23 @@ +{# Append DHCPv6 agents #} +{# Create a program entry for each DHCPv6 relay agent instance #} +{% for vlan_name in VLAN_INTERFACE %} +{% if DHCP_RELAY and vlan_name in DHCP_RELAY and 'dhcpv6_servers' in DHCP_RELAY[vlan_name] %} +{% for dhcpv6_server in DHCP_RELAY[vlan_name]['dhcpv6_servers'] %} +{% if dhcpv6_server | ipv6 %} +{% set _dummy = relay_for_ipv6.update({'flag': True}) %} +{% endif %} +{% endfor %} +{% if relay_for_ipv6.flag %} +{% set _dummy = relay_for_ipv6.update({'flag': False}) %} +[program:dhcp6relay] +command=/usr/sbin/dhcp6relay + +priority=3 +autostart=false +autorestart=false +stdout_logfile=syslog +stderr_logfile=syslog + +{% endif %} +{% endif %} +{% endfor %} diff --git a/dockers/docker-dhcp-relay/docker-dhcp-relay.supervisord.conf.j2 b/dockers/docker-dhcp-relay/docker-dhcp-relay.supervisord.conf.j2 index 6a12693efc64..e9c043dc2600 100644 --- a/dockers/docker-dhcp-relay/docker-dhcp-relay.supervisord.conf.j2 +++ b/dockers/docker-dhcp-relay/docker-dhcp-relay.supervisord.conf.j2 @@ -41,122 +41,27 @@ dependent_startup_wait_for=rsyslogd:running {# If our configuration has VLANs... #} {% if VLAN_INTERFACE %} -{# Count how many VLANs require a DHCP relay agent... #} -{% set num_relays = { 'count': 0 } %} +{% set ipv4_num_relays = { 'count': 0 } %} +{% set ipv6_num_relays = { 'count': 0 } %} {% for vlan_name in VLAN_INTERFACE %} {% if VLAN and vlan_name in VLAN and 'dhcp_servers' in VLAN[vlan_name] and VLAN[vlan_name]['dhcp_servers']|length > 0 %} -{% set _dummy = num_relays.update({'count': num_relays.count + 1}) %} -{% endif %} -{% endfor %} -{# If one or more of the VLANs require a DHCP relay agent... #} -{% if num_relays.count > 0 %} -[group:isc-dhcp-relay] -programs= -{%- set add_preceding_comma = { 'flag': False } %} -{% for vlan_name in VLAN_INTERFACE %} -{% if VLAN and vlan_name in VLAN and 'dhcp_servers' in VLAN[vlan_name] and VLAN[vlan_name]['dhcp_servers']|length > 0 %} -{% if add_preceding_comma.flag %},{% endif %} -{% set _dummy = add_preceding_comma.update({'flag': True}) %} -isc-dhcp-relay-{{ vlan_name }} -{%- endif %} -{% endfor %} - - -{# Create a program entry for each DHCP relay agent instance #} -{% set relay_for_ipv4 = { 'flag': False } %} -{% for vlan_name in VLAN_INTERFACE %} -{% if VLAN and vlan_name in VLAN and 'dhcp_servers' in VLAN[vlan_name] and VLAN[vlan_name]['dhcp_servers']|length > 0 %} -{% for dhcp_server in VLAN[vlan_name]['dhcp_servers'] %} -{% if dhcp_server | ipv4 %} -{% set _dummy = relay_for_ipv4.update({'flag': True}) %} -{% endif %} -{% endfor %} -{% if relay_for_ipv4.flag %} -{% set _dummy = relay_for_ipv4.update({'flag': False}) %} -[program:isc-dhcp-relay-{{ vlan_name }}] -{# We treat this VLAN as a downstream interface (-id), as we only want to listen for requests #} -command=/usr/sbin/dhcrelay -d -m discard -a %%h:%%p %%P --name-alias-map-file /tmp/port-name-alias-map.txt -id {{ vlan_name }} -{#- si option to use intf addr in relay #} -{% if DEVICE_METADATA['localhost']['deployment_id'] == '8' %} -si{% endif -%} -{#- We treat all other interfaces as upstream interfaces (-iu), as we only want to listen for replies #} -{% for (name, prefix) in VLAN_INTERFACE|pfx_filter %} -{% if prefix | ipv4 and name != vlan_name %} -iu {{ name }}{% endif -%} -{% endfor %} -{% for (name, prefix) in INTERFACE|pfx_filter %} -{% if prefix | ipv4 %} -iu {{ name }}{% endif -%} -{% endfor %} -{% for (name, prefix) in PORTCHANNEL_INTERFACE|pfx_filter %} -{% if prefix | ipv4 %} -iu {{ name }}{% endif -%} -{% endfor %} -{% for dhcp_server in VLAN[vlan_name]['dhcp_servers'] %} -{%- if dhcp_server | ipv4 %} {{ dhcp_server }}{% endif -%} -{% endfor %} - -priority=3 -autostart=false -autorestart=false -stdout_logfile=syslog -stderr_logfile=syslog -dependent_startup=true -dependent_startup_wait_for=start:exited - +{% set _dummy = ipv4_num_relays.update({'count': ipv4_num_relays.count + 1}) %} {% endif %} +{% if DHCP_RELAY and vlan_name in DHCP_RELAY and 'dhcpv6_servers' in DHCP_RELAY[vlan_name] %} +{% set _dummy = ipv6_num_relays.update({'count': ipv6_num_relays.count + 1}) %} {% endif %} {% endfor %} +{# If one or more of the VLANs require a DHCP relay agent... #} +{% if ipv4_num_relays.count > 0 or ipv6_num_relays.count > 0 %} +{% include 'dhcp-relay.programs.j2' %} -[group:dhcpmon] -programs= -{%- set add_preceding_comma = { 'flag': False } %} -{% for vlan_name in VLAN_INTERFACE %} -{% if VLAN and vlan_name in VLAN and 'dhcp_servers' in VLAN[vlan_name] and VLAN[vlan_name]['dhcp_servers']|length > 0 %} -{% if add_preceding_comma.flag %},{% endif %} -{% set _dummy = add_preceding_comma.update({'flag': True}) %} -dhcpmon-{{ vlan_name }} -{%- endif %} -{% endfor %} - - -{# Create a program entry for each DHCP MONitor instance #} {% set relay_for_ipv4 = { 'flag': False } %} +{% set relay_for_ipv6 = { 'flag': False } %} {% for vlan_name in VLAN_INTERFACE %} -{% if VLAN and vlan_name in VLAN and 'dhcp_servers' in VLAN[vlan_name] and VLAN[vlan_name]['dhcp_servers']|length > 0 %} -{% for dhcp_server in VLAN[vlan_name]['dhcp_servers'] %} -{% if dhcp_server | ipv4 %} -{% set _dummy = relay_for_ipv4.update({'flag': True}) %} -{% endif %} -{% endfor %} -{% if relay_for_ipv4.flag %} -{% set _dummy = relay_for_ipv4.update({'flag': False}) %} -[program:dhcpmon-{{ vlan_name }}] -{# We treat this VLAN as a downstream interface (-id), as we only want to listen for requests #} -command=/usr/sbin/dhcpmon -id {{ vlan_name }} -{#- We treat all other interfaces as upstream interfaces (-iu), as we only want to listen for replies #} -{% for (name, prefix) in VLAN_INTERFACE|pfx_filter %} -{% if prefix | ipv4 and name != vlan_name %} -iu {{ name }}{% endif -%} -{% endfor %} -{% for (name, prefix) in INTERFACE|pfx_filter %} -{% if prefix | ipv4 %} -iu {{ name }}{% endif -%} -{% endfor %} -{% for (name, prefix) in PORTCHANNEL_INTERFACE|pfx_filter %} -{% if prefix | ipv4 %} -iu {{ name }}{% endif -%} -{% endfor %} -{% if MGMT_INTERFACE %} -{% for (name, prefix) in MGMT_INTERFACE|pfx_filter %} -{% if prefix | ipv4 %} -im {{ name }}{% endif -%} -{% endfor %} -{% endif %} - -priority=4 -autostart=false -autorestart=false -stdout_logfile=syslog -stderr_logfile=syslog -dependent_startup=true -dependent_startup_wait_for=isc-dhcp-relay-{{ vlan_name }}:running - -{% endif %} -{% endif %} +{% include 'dhcpv4-relay.agents.j2' %} {% endfor %} +{% include 'dhcpv6-relay.agents.j2' %} +{% include 'dhcp-relay.monitors.j2' %} {% endif %} {% endif %} diff --git a/dockers/docker-dhcp-relay/start.sh b/dockers/docker-dhcp-relay/start.sh index 86ba91147ef5..2488137378a9 100755 --- a/dockers/docker-dhcp-relay/start.sh +++ b/dockers/docker-dhcp-relay/start.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # If our supervisor config has entries in the "isc-dhcp-relay" group... -if [ $(supervisorctl status | grep -c "^isc-dhcp-relay:") -gt 0 ]; then +if [ $(supervisorctl status | grep -c "^dhcp-relay:") -gt 0 ]; then # Wait for all interfaces to come up and be assigned IPv4 addresses before # starting the DHCP relay agent(s). If an interface the relay should listen # on is down, the relay agent will not start. If an interface the relay diff --git a/dockers/docker-dhcp-relay/wait_for_intf.sh.j2 b/dockers/docker-dhcp-relay/wait_for_intf.sh.j2 index 568f128b3772..b224a697b5ba 100644 --- a/dockers/docker-dhcp-relay/wait_for_intf.sh.j2 +++ b/dockers/docker-dhcp-relay/wait_for_intf.sh.j2 @@ -38,3 +38,8 @@ wait_until_iface_ready {{ name }} {{ prefix }} wait_until_iface_ready {{ name }} {{ prefix }} {% endif %} {% endfor %} + +# Wait 10 seconds for the rest of interfaces to get added/populated. +# dhcrelay listens on each of the interfaces (in addition to the port +# channels and vlan interfaces) +sleep 10 diff --git a/rules/dhcp6relay.mk b/rules/dhcp6relay.mk new file mode 100644 index 000000000000..7f1ab3a1e354 --- /dev/null +++ b/rules/dhcp6relay.mk @@ -0,0 +1,12 @@ +# SONiC DHCPV6 RELAY Package + +SONIC_DHCP6RELAY_VERSION = 1.0.0-0 +SONIC_DHCP6RELAY_PKG_NAME = dhcp6relay + +SONIC_DHCP6RELAY = sonic-$(SONIC_DHCP6RELAY_PKG_NAME)_$(SONIC_DHCP6RELAY_VERSION)_$(CONFIGURED_ARCH).deb +$(SONIC_DHCP6RELAY)_DEPENDS = $(LIBSWSSCOMMON) $(LIBHIREDIS) $(LIBSWSSCOMMON_DEV) $(LIBHIREDIS_DEV) +$(SONIC_DHCP6RELAY)_SRC_PATH = $(SRC_PATH)/$(SONIC_DHCP6RELAY_PKG_NAME) +SONIC_DPKG_DEBS += $(SONIC_DHCP6RELAY) + +SONIC_DHCP6RELAY_DBG = sonic-$(SONIC_DHCP6RELAY_PKG_NAME)-dbg_$(SONIC_DHCP6RELAY_VERSION)_amd64.deb +$(eval $(call add_derived_package,$(SONIC_DHCP6RELAY),$(SONIC_DHCP6RELAY_DBG))) diff --git a/rules/docker-dhcp-relay.mk b/rules/docker-dhcp-relay.mk index 520e243d6266..4a724443fb88 100644 --- a/rules/docker-dhcp-relay.mk +++ b/rules/docker-dhcp-relay.mk @@ -6,9 +6,9 @@ DOCKER_DHCP_RELAY_DBG = $(DOCKER_DHCP_RELAY_STEM)-$(DBG_IMAGE_MARK).gz $(DOCKER_DHCP_RELAY)_PATH = $(DOCKERS_PATH)/$(DOCKER_DHCP_RELAY_STEM) -$(DOCKER_DHCP_RELAY)_DEPENDS += $(ISC_DHCP_RELAY) $(REDIS_TOOLS) $(SONIC_DHCPMON) +$(DOCKER_DHCP_RELAY)_DEPENDS += $(ISC_DHCP_RELAY) $(REDIS_TOOLS) $(SONIC_DHCPMON) $(SONIC_DHCP6RELAY) $(LIBSWSSCOMMON) $(DOCKER_DHCP_RELAY)_DBG_DEPENDS = $($(DOCKER_CONFIG_ENGINE_STRETCH)_DBG_DEPENDS) -$(DOCKER_DHCP_RELAY)_DBG_DEPENDS += $(ISC_DHCP_RELAY_DBG) +$(DOCKER_DHCP_RELAY)_DBG_DEPENDS += $(ISC_DHCP_RELAY_DBG) $(SONIC_DHCP6RELAY_DBG) $(DOCKER_DHCP_RELAY)_DBG_IMAGE_PACKAGES = $($(DOCKER_CONFIG_ENGINE_STRETCH)_DBG_IMAGE_PACKAGES) diff --git a/sonic-dhcp-relay b/sonic-dhcp-relay new file mode 160000 index 000000000000..e2cd5851ddd9 --- /dev/null +++ b/sonic-dhcp-relay @@ -0,0 +1 @@ +Subproject commit e2cd5851ddd9b6496b59b72e75aaba8e4d7f284d diff --git a/src/dhcp6relay/.gitignore b/src/dhcp6relay/.gitignore new file mode 100644 index 000000000000..9d09ae6b3f1a --- /dev/null +++ b/src/dhcp6relay/.gitignore @@ -0,0 +1,5 @@ +debian/* +!debian/changelog +!debian/compat +!debian/control +!debian/rules diff --git a/src/dhcp6relay/Makefile b/src/dhcp6relay/Makefile new file mode 100644 index 000000000000..8b5740fd787e --- /dev/null +++ b/src/dhcp6relay/Makefile @@ -0,0 +1,42 @@ +RM := rm -rf +DHCP6RELAY_TARGET := dhcp6relay +CP := cp +MKDIR := mkdir +CC := g++ +MV := mv +LIBS := -levent -lhiredis -lswsscommon -pthread -lboost_thread -lboost_system +CFLAGS += -Wall -std=c++14 -fPIE -I$(PWD)/../sonic-swss-common/common +PWD := $(shell pwd) + +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(strip $(C_DEPS)),) +-include $(C_DEPS) $(OBJS) +endif +endif + +-include src/subdir.mk + +all: sonic-dhcp6relay + +sonic-dhcp6relay: $(OBJS) + @echo 'Building target: $@' + @echo 'Invoking: G++ Linker' + $(CC) $(LDFLAGS) -o $(DHCP6RELAY_TARGET) $(OBJS) $(LIBS) + @echo 'Finished building target: $@' + @echo ' ' + +install: + $(MKDIR) -p $(DESTDIR)/usr/sbin + $(MV) $(DHCP6RELAY_TARGET) $(DESTDIR)/usr/sbin + +deinstall: + $(RM) $(DESTDIR)/usr/sbin/$(DHCP6RELAY_TARGET) + $(RM) -rf $(DESTDIR)/usr/sbin + +clean: + -$(RM) $(EXECUTABLES) $(C_DEPS) $(OBJS) $(DHCP6RELAY_TARGET) + -@echo ' ' + +.PHONY: all clean dependents + + diff --git a/src/dhcp6relay/debian/changelog b/src/dhcp6relay/debian/changelog new file mode 100644 index 000000000000..aec73a095801 --- /dev/null +++ b/src/dhcp6relay/debian/changelog @@ -0,0 +1,5 @@ +sonic-dhcp6relay (1.0.0-0) UNRELEASED; urgency=medium + + * Initial release. + + -- Kelly Yeh Fri, 15 Oct 2021 00:00:00 +1000 diff --git a/src/dhcp6relay/debian/compat b/src/dhcp6relay/debian/compat new file mode 100644 index 000000000000..ec635144f600 --- /dev/null +++ b/src/dhcp6relay/debian/compat @@ -0,0 +1 @@ +9 diff --git a/src/dhcp6relay/debian/control b/src/dhcp6relay/debian/control new file mode 100644 index 000000000000..fb82be7c911f --- /dev/null +++ b/src/dhcp6relay/debian/control @@ -0,0 +1,27 @@ +Source: sonic-dhcp6relay +Section: devel +Priority: optional +Maintainer: Kelly Yeh +Build-Depends: debhelper (>= 8.0.0), + dh-systemd +Standards-Version: 3.9.3 +Homepage: https://github.com/Azure/sonic-buildimage +XS-Go-Import-Path: github.com/Azure/sonic-buildimage + +Package: sonic-dhcp6relay +Architecture: any +Built-Using: ${misc:Built-Using} +Depends: libevent-2.0-5, + libboost-thread1.55.0, + libboost-system1.55.0 +Description: SONiC DHCPv6 Relay + +Package: sonic-dhcp6relay-dbg +Architecture: any +Section: debug +Priority: extra +Built-Using: ${misc:Built-Using} +Depends: libevent-2.0-5, + libboost-thread1.55.0, + libboost-system1.55.0 +Description: SONiC DHCPv6 Relay debug symbols diff --git a/src/dhcp6relay/debian/rules b/src/dhcp6relay/debian/rules new file mode 100755 index 000000000000..ed8a2397a6d4 --- /dev/null +++ b/src/dhcp6relay/debian/rules @@ -0,0 +1,12 @@ +#!/usr/bin/make -f + +export DEB_BUILD_MAINT_OPTIONS=hardening=+all + +%: + dh $@ --parallel + +override_dh_strip: + dh_strip --dbg-package=sonic-dhcp6relay-dbg + +override_dh_auto_install: + dh_auto_install --destdir=debian/sonic-dhcp6relay diff --git a/src/dhcp6relay/src/configInterface.cpp b/src/dhcp6relay/src/configInterface.cpp new file mode 100644 index 000000000000..dfe6031e0468 --- /dev/null +++ b/src/dhcp6relay/src/configInterface.cpp @@ -0,0 +1,151 @@ +#include +#include +#include +#include "configInterface.h" + +constexpr auto DEFAULT_TIMEOUT_MSEC = 1000; + +bool pollSwssNotifcation = true; +std::shared_ptr mSwssThreadPtr; + +std::shared_ptr configDbPtr = std::make_shared (4, "localhost", 6379, 0); +swss::SubscriberStateTable ipHelpersTable(configDbPtr.get(), "DHCP_RELAY"); +swss::Select swssSelect; + +/** + * @code void initialize_swss() + * + * @brief initialize DB tables and start SWSS listening thread + * + * @return none + */ +void initialize_swss(std::vector *vlans) +{ + try { + swssSelect.addSelectable(&ipHelpersTable); + get_dhcp(vlans); + mSwssThreadPtr = std::make_shared (&handleSwssNotification, vlans); + } + catch (const std::bad_alloc &e) { + syslog(LOG_ERR, "Failed allocate memory. Exception details: %s", e.what()); + } +} + +/** + * @code void deinitialize_swss() + * + * @brief deinitialize DB interface and join SWSS listening thread + * + * @return none + */ +void deinitialize_swss() +{ + stopSwssNotificationPoll(); + mSwssThreadPtr->interrupt(); +} + + +/** + * @code void get_dhcp(std::vector *vlans) + * + * @brief initialize and get vlan table information from DHCP_RELAY + * + * @return none + */ +void get_dhcp(std::vector *vlans) { + swss::Selectable *selectable; + int ret = swssSelect.select(&selectable, DEFAULT_TIMEOUT_MSEC); + if (ret == swss::Select::ERROR) { + syslog(LOG_WARNING, "Select: returned ERROR"); + } else if (ret == swss::Select::TIMEOUT) { + } + if (selectable == static_cast (&ipHelpersTable)) { + handleRelayNotification(ipHelpersTable, vlans); + } +} +/** + * @code void handleSwssNotification(std::vector *vlans) + * + * @brief main thread for handling SWSS notification + * + * @param context list of vlans/argument config that contains strings of server and option + * + * @return none + */ +void handleSwssNotification(std::vector *vlans) +{ + while (pollSwssNotifcation) { + get_dhcp(vlans); + } +} + +/** + * @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::vector *vlans) + * + * @brief handles DHCPv6 relay configuration change notification + * + * @param ipHelpersTable DHCP table + * @param vlans list of vlans/argument config that contains strings of server and option + * + * @return none + */ +void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::vector *vlans) +{ + std::deque entries; + + ipHelpersTable.pops(entries); + processRelayNotification(entries, vlans); +} + +/** + * @code void processRelayNotification(std::deque &entries, std::vector *vlans) + * + * @brief process DHCPv6 relay servers and options configuration change notification + * + * @param entries queue of std::tuple> entries in DHCP table + * @param vlans list of vlans/argument config that contains strings of server and option + * + * @return none + */ +void processRelayNotification(std::deque &entries, std::vector *vlans) +{ + std::vector servers; + + for (auto &entry: entries) { + std::string vlan = kfvKey(entry); + std::string operation = kfvOp(entry); + std::vector fieldValues = kfvFieldsValues(entry); + + relay_config intf; + intf.is_option_79 = true; + intf.interface = vlan; + for (auto &fieldValue: fieldValues) { + std::string f = fvField(fieldValue); + std::string v = fvValue(fieldValue); + if(f == "dhcpv6_servers") { + std::stringstream ss(v); + while (ss.good()) { + std::string substr; + getline(ss, substr, ','); + intf.servers.push_back(substr); + } + syslog(LOG_DEBUG, "key: %s, Operation: %s, f: %s, v: %s", vlan.c_str(), operation.c_str(), f.c_str(), v.c_str()); + } + if(f == "dhcpv6_option|rfc6939_support" && v == "false") { + intf.is_option_79 = false; + } + } + vlans->push_back(intf); + } +} + +/** +*@code stopSwssNotificationPoll +* +*@brief stop SWSS listening thread +* +*@return none +*/ +void stopSwssNotificationPoll() { + pollSwssNotifcation = false; +}; diff --git a/src/dhcp6relay/src/configInterface.d b/src/dhcp6relay/src/configInterface.d new file mode 100644 index 000000000000..d803bf1a54de --- /dev/null +++ b/src/dhcp6relay/src/configInterface.d @@ -0,0 +1,44 @@ +src/configInterface.o: src/configInterface.cpp src/configInterface.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/subscriberstatetable.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/dbconnector.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/consumertablebase.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/table.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/redisreply.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/rediscommand.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/redisselect.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/selectable.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/redispipeline.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/schema.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/redistran.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/logger.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/select.h src/relay.h + +src/configInterface.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/subscriberstatetable.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/dbconnector.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/consumertablebase.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/table.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/redisreply.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/rediscommand.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/redisselect.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/selectable.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/redispipeline.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/schema.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/redistran.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/logger.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/select.h: + +src/relay.h: diff --git a/src/dhcp6relay/src/configInterface.h b/src/dhcp6relay/src/configInterface.h new file mode 100644 index 000000000000..20b0912c5cef --- /dev/null +++ b/src/dhcp6relay/src/configInterface.h @@ -0,0 +1,75 @@ +#include +#include "subscriberstatetable.h" +#include "select.h" +#include "relay.h" + +/** + * @code void initialize_swss() + * + * @brief initialize DB tables and start SWSS listening thread + * + * @return none + */ +void initialize_swss(std::vector *vlans); + +/** + * @code void deinitialize_swss() + * + * @brief deinitialize DB interface and join SWSS listening thread + * + * @return none + */ +void deinitialize_swss(); + +/** + * @code void get_dhcp(std::vector *vlans) + * + * @brief initialize and get vlan information from DHCP_RELAY + * + * @return none + */ +void get_dhcp(std::vector *vlans); + +/** + * @code void handleSwssNotification(std::vector *vlans) + * + * @brief main thread for handling SWSS notification + * + * @param vlans list of vlans/argument config that contains strings of server and option + * + * @return none + */ +void handleSwssNotification(std::vector *vlans); + +/** + * @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::vector *vlans) + * + * @brief handles DHCPv6 relay configuration change notification + * + * @param ipHelpersTable DHCP table + * @param vlans list of vlans/argument config that contains strings of server and option + * + * @return none + */ +void handleRelayNotification(swss::SubscriberStateTable &configMuxTable, std::vector *vlans); + +/** + * @code void processRelayNotification(std::deque &entries, std::vector *vlans) + * + * @brief process DHCPv6 relay servers and options configuration change notification + * + * @param entries queue of std::tuple> entries in DHCP table + * @param context list of vlans/argument config that contains strings of server and option + * + * @return none + */ +void processRelayNotification(std::deque &entries, std::vector *vlans); + +/** +*@code stopSwssNotificationPoll +* +*@brief stop SWSS listening thread +* +*@return none +*/ +void stopSwssNotificationPoll(); diff --git a/src/dhcp6relay/src/main.cpp b/src/dhcp6relay/src/main.cpp new file mode 100644 index 000000000000..6d0fb29c050f --- /dev/null +++ b/src/dhcp6relay/src/main.cpp @@ -0,0 +1,17 @@ +#include +#include +#include "configInterface.h" + +int main(int argc, char *argv[]) { + try { + std::vector vlans; + initialize_swss(&vlans); + loop_relay(&vlans); + } + catch (std::exception &e) + { + syslog(LOG_ERR, "An exception occurred.\n"); + return 1; + } + return 0; +} diff --git a/src/dhcp6relay/src/main.d b/src/dhcp6relay/src/main.d new file mode 100644 index 000000000000..05e7fd2367f0 --- /dev/null +++ b/src/dhcp6relay/src/main.d @@ -0,0 +1,44 @@ +src/main.o: src/main.cpp src/configInterface.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/subscriberstatetable.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/dbconnector.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/consumertablebase.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/table.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/redisreply.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/rediscommand.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/redisselect.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/selectable.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/redispipeline.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/schema.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/redistran.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/logger.h \ + /sonic/src/dhcp6relay/../sonic-swss-common/common/select.h src/relay.h + +src/configInterface.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/subscriberstatetable.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/dbconnector.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/consumertablebase.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/table.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/redisreply.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/rediscommand.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/redisselect.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/selectable.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/redispipeline.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/schema.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/redistran.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/logger.h: + +/sonic/src/dhcp6relay/../sonic-swss-common/common/select.h: + +src/relay.h: diff --git a/src/dhcp6relay/src/relay.cpp b/src/dhcp6relay/src/relay.cpp new file mode 100644 index 000000000000..c4589774bfa9 --- /dev/null +++ b/src/dhcp6relay/src/relay.cpp @@ -0,0 +1,816 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dbconnector.h" +#include "configInterface.h" +#include "redisclient.h" + + +struct event *listen_event; +struct event *server_listen_event; +struct event_base *base; +struct event *ev_sigint; +struct event *ev_sigterm; +static std::string counter_table = "DHCPv6_COUNTER_TABLE|"; +swss::DBConnector state_db(6, "localhost", 6379, 0); +swss::RedisClient m_stateDbRedisClient(&state_db); + +/* DHCPv6 filter */ +/* sudo tcpdump -dd "ip6 dst ff02::1:2 && udp dst port 547" */ + +static struct sock_filter ether_relay_filter[] = { + + { 0x28, 0, 0, 0x0000000c }, + { 0x15, 0, 13, 0x000086dd }, + { 0x20, 0, 0, 0x00000026 }, + { 0x15, 0, 11, 0xff020000 }, + { 0x20, 0, 0, 0x0000002a }, + { 0x15, 0, 9, 0x00000000 }, + { 0x20, 0, 0, 0x0000002e }, + { 0x15, 0, 7, 0x00000000 }, + { 0x20, 0, 0, 0x00000032 }, + { 0x15, 0, 5, 0x00010002 }, + { 0x30, 0, 0, 0x00000014 }, + { 0x15, 0, 3, 0x00000011 }, + { 0x28, 0, 0, 0x00000038 }, + { 0x15, 0, 1, 0x00000223 }, + { 0x6, 0, 0, 0x00040000 }, + { 0x6, 0, 0, 0x00000000 }, +}; +const struct sock_fprog ether_relay_fprog = { + lengthof(ether_relay_filter), + ether_relay_filter +}; + +/* DHCPv6 Counter */ +uint64_t counters[DHCPv6_MESSAGE_TYPE_COUNT]; +std::map counterMap = {{0, "Unknown"}, + {1, "Solicit"}, + {2, "Advertise"}, + {3, "Request"}, + {4, "Confirm"}, + {5, "Renew"}, + {6, "Rebind"}, + {7, "Reply"}, + {8, "Release"}, + {9, "Decline"}, + {12, "Relay-Forward"}, + {13, "Relay-Reply"}}; + +/** + * @code void initialize_counter(std::string counterVlan); + * + * @brief initialize the counter by each Vlan + * + * @param counterVlan counter table with interface name + * + * @return none + */ +void initialize_counter(std::string counterVlan) { + m_stateDbRedisClient.hset(counterVlan, "Unknown", toString(counters[DHCPv6_MESSAGE_TYPE_UNKNOWN])); + m_stateDbRedisClient.hset(counterVlan, "Solicit", toString(counters[DHCPv6_MESSAGE_TYPE_SOLICIT])); + m_stateDbRedisClient.hset(counterVlan, "Advertise", toString(counters[DHCPv6_MESSAGE_TYPE_ADVERTISE])); + m_stateDbRedisClient.hset(counterVlan, "Request", toString(counters[DHCPv6_MESSAGE_TYPE_REQUEST])); + m_stateDbRedisClient.hset(counterVlan, "Confirm", toString(counters[DHCPv6_MESSAGE_TYPE_CONFIRM])); + m_stateDbRedisClient.hset(counterVlan, "Renew", toString(counters[DHCPv6_MESSAGE_TYPE_RENEW])); + m_stateDbRedisClient.hset(counterVlan, "Rebind", toString(counters[DHCPv6_MESSAGE_TYPE_REBIND])); + m_stateDbRedisClient.hset(counterVlan, "Reply", toString(counters[DHCPv6_MESSAGE_TYPE_REPLY])); + m_stateDbRedisClient.hset(counterVlan, "Release", toString(counters[DHCPv6_MESSAGE_TYPE_RELEASE])); + m_stateDbRedisClient.hset(counterVlan, "Decline", toString(counters[DHCPv6_MESSAGE_TYPE_DECLINE])); + m_stateDbRedisClient.hset(counterVlan, "Relay-Forward", toString(counters[DHCPv6_MESSAGE_TYPE_RELAY_FORW])); + m_stateDbRedisClient.hset(counterVlan, "Relay-Reply", toString(counters[DHCPv6_MESSAGE_TYPE_RELAY_REPL])); +} + +/** + * @code void update_counter(std::string CounterVlan, uint8_t msg_type); + * + * @brief update the counter in state_db with count of each DHCPv6 message type + * + * @param counterVlan counter table with interface name + * @param msg_type dhcpv6 message type to be updated in counter + * + * @return none + */ +void update_counter(std::string counterVlan, uint8_t msg_type) { + m_stateDbRedisClient.hset(counterVlan, counterMap.find(msg_type)->second, toString(counters[msg_type])); +} + +/** + * @code std::string toString(uint16_t count); + * + * @brief convert uint16_t to string + * + * @param count count of messages in counter + * + * @return count in string + */ +std::string toString(uint16_t count) { + std::stringstream ss; + ss << count; + std::string countValue = ss.str(); + return countValue; +} + +/** + * @code const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end); + * + * @brief parse through ethernet frame + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return ether_header end of ethernet header position + */ +const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end) { + (*out_end) = buffer + sizeof(struct ether_header); + return (const struct ether_header *)buffer; +} + +/** + * @code const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end); + * + * @brief parse through ipv6 header + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return ip6_hdr end of ipv6 header position + */ +const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end) { + (*out_end) = buffer + sizeof(struct ip6_hdr); + return (struct ip6_hdr *)buffer; +} + +/** + * @code const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end); + * + * @brief parse through udp header + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return udphdr end of udp header position + */ +const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end) { + (*out_end) = buffer + sizeof(struct udphdr); + return (const struct udphdr *)buffer; +} + +/** + * @code const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer); + * + * @brief parse through dhcpv6 header + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return dhcpv6_msg end of dhcpv6 header position + */ +const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer) { + return (const struct dhcpv6_msg *)buffer; +} + +/** + * @code const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer); + * + * @brief parse through dhcpv6 relay message + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return dhcpv6_relay_msg start of dhcpv6 relay message or end of dhcpv6 message type position + */ +const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer) { + return (const struct dhcpv6_relay_msg *)buffer; +} + +/** + * @code const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end); + * + * @brief parse through dhcpv6 option + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return dhcpv6_option end of dhcpv6 message option + */ +const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end) { + uint32_t size = 4; // option-code + option-len + size += ntohs(*(uint16_t *)(buffer + 2)); + (*out_end) = buffer + size; + + return (const struct dhcpv6_option *)buffer; +} + +/** + * @code void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type); + * + * @brief send udp packet + * + * @param *buffer message buffer + * @param sockaddr_in6 target target socket + * @param n length of message + * @param relay_config *config pointer to relay_config + * @param uint8_t msg_type message type of dhcpv6 option of relayed message + * + * @return dhcpv6_option end of dhcpv6 message option + */ +void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type) { + std::string counterVlan = counter_table; + if(sendto(sock, buffer, n, 0, (const struct sockaddr *)&target, sizeof(target)) == -1) + syslog(LOG_ERR, "sendto: Failed to send to target address\n"); + else if (counterMap.find(msg_type) != counterMap.end()) { + counters[msg_type]++; + update_counter(counterVlan.append(config->interface), msg_type); + } else { + syslog(LOG_WARNING, "unexpected message type %d(0x%x)\n", msg_type, msg_type); + } +} + +/** + * @code relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length); + * + * @brief embed the DHCPv6 message received into DHCPv6 relay forward message + * + * @param buffer pointer to buffer + * @param msg pointer to parsed DHCPv6 message + * @param msg_length length of DHCPv6 message + * + * @return none + */ +void relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length) { + struct dhcpv6_option option; + option.option_code = htons(OPTION_RELAY_MSG); + option.option_length = htons(msg_length); + memcpy(buffer, &option, sizeof(struct dhcpv6_option)); + memcpy(buffer + sizeof(struct dhcpv6_option), msg, msg_length); +} + +/** + * @code sock_open(int ifindex, const struct sock_fprog *fprog); + * + * @brief prepare L2 socket to attach to "udp and port 547" filter + * + * @param ifindex interface index + * @param fprog bpf filter "udp and port 547" + * + * @return socket descriptor + */ +int sock_open(int ifindex, const struct sock_fprog *fprog) +{ + if (!ifindex) { + errno = EINVAL; + return -1; + } + + int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (s == -1) { + syslog(LOG_ERR, "socket: Failed to create socket\n"); + return -1; + } + + struct sockaddr_ll sll = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_ALL), + .sll_ifindex = ifindex + }; + + if (bind(s, (struct sockaddr *)&sll, sizeof sll) == -1) { + syslog(LOG_ERR, "bind: Failed to bind to specified interface\n"); + (void) close(s); + return -1; + } + + if (fprog && setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, fprog, sizeof *fprog) == -1) + { + syslog(LOG_ERR, "setsockopt: Failed to attach filter\n"); + (void) close(s); + return -1; + } + + return s; +} + +/** + * @code prepare_relay_config(relay_config *interface_config, int local_sock, int filter); + * + * @brief prepare for specified relay interface config: server and link address + * + * @param interface_config pointer to relay config to be prepared + * @param local_sock L3 socket used for relaying messages + * @param filter socket attached with filter + * + * @return none + */ +void prepare_relay_config(relay_config *interface_config, int *local_sock, int filter) { + struct ifaddrs *ifa, *ifa_tmp; + sockaddr_in6 non_link_local; + sockaddr_in6 link_local; + + interface_config->local_sock = *local_sock; + interface_config->filter = filter; + + for(auto server: interface_config->servers) { + sockaddr_in6 tmp; + if(inet_pton(AF_INET6, server.c_str(), &tmp.sin6_addr) != 1) + { + syslog(LOG_WARNING, "inet_pton: Failed to convert IPv6 address\n"); + } + tmp.sin6_family = AF_INET6; + tmp.sin6_flowinfo = 0; + tmp.sin6_port = htons(RELAY_PORT); + tmp.sin6_scope_id = 0; + interface_config->servers_sock.push_back(tmp); + } + + if (getifaddrs(&ifa) == -1) { + syslog(LOG_WARNING, "getifaddrs: Unable to get network interfaces\n"); + exit(1); + } + + ifa_tmp = ifa; + while (ifa_tmp) { + if (ifa_tmp->ifa_addr->sa_family == AF_INET6) { + struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr; + if((strcmp(ifa_tmp->ifa_name, interface_config->interface.c_str()) == 0) && !IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { + non_link_local = *in6; + break; + } + if((strcmp(ifa_tmp->ifa_name, interface_config->interface.c_str()) == 0) && IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { + link_local = *in6; + } + } + ifa_tmp = ifa_tmp->ifa_next; + } + freeifaddrs(ifa); + + if(!IN6_IS_ADDR_LINKLOCAL(&non_link_local.sin6_addr)) { + interface_config->link_address = non_link_local; + } + else { + interface_config->link_address = link_local; + } +} + +/** + * @code prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index); + * + * @brief prepare L3 socket for sending + * + * @param local_sock pointer to socket binded to global address for relaying client message to server and listening for server message + * @param server_sock pointer to socket binded to link_local address for relaying server message to client + * @param index scope id of interface + * + * @return none + */ +void prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index) { + struct ifaddrs *ifa, *ifa_tmp; + sockaddr_in6 addr; + sockaddr_in6 ll_addr; + memset(&addr, 0, sizeof(addr)); + memset(&ll_addr, 0, sizeof(ll_addr)); + + if ((*local_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { + syslog(LOG_ERR, "socket: Failed to create socket\n"); + } + + if ((*server_sock= socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { + syslog(LOG_ERR, "socket: Failed to create socket\n"); + } + + + if (getifaddrs(&ifa) == -1) { + syslog(LOG_WARNING, "getifaddrs: Unable to get network interfaces\n"); + exit(1); + } + + ifa_tmp = ifa; + while (ifa_tmp) { + if (ifa_tmp->ifa_addr->sa_family == AF_INET6) { + struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr; + if((strcmp(ifa_tmp->ifa_name, config->interface.c_str()) == 0) && !IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { + in6->sin6_family = AF_INET6; + in6->sin6_port = htons(RELAY_PORT); + addr = *in6; + } + if((strcmp(ifa_tmp->ifa_name, config->interface.c_str()) == 0) && IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { + in6->sin6_family = AF_INET6; + in6->sin6_port = htons(RELAY_PORT); + ll_addr = *in6; + } + } + ifa_tmp = ifa_tmp->ifa_next; + } + freeifaddrs(ifa); + + if (bind(*local_sock, (sockaddr *)&addr, sizeof(addr)) == -1) { + syslog(LOG_ERR, "bind: Failed to bind to socket\n"); + } + + if (bind(*server_sock, (sockaddr *)&ll_addr, sizeof(addr)) == -1) { + syslog(LOG_ERR, "bind: Failed to bind to socket\n"); + } +} + + +/** + * @code relay_client(int sock, const uint8_t *msg, int32_t len, ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config); + * + * @brief construct relay-forward message + * + * @param sock L3 socket for sending data to servers + * @param msg pointer to dhcpv6 message header position + * @param len size of data received + * @param ip_hdr pointer to IPv6 header + * @param ether_hdr pointer to Ethernet header + * @param config pointer to the relay interface config + * + * @return none + */ +void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config) { + static uint8_t buffer[4096]; + auto current_buffer_position = buffer; + dhcpv6_relay_msg new_message; + new_message.msg_type = DHCPv6_MESSAGE_TYPE_RELAY_FORW; + memcpy(&new_message.peer_address, &ip_hdr->ip6_src, sizeof(in6_addr)); + new_message.hop_count = 0; + + memcpy(&new_message.link_address, &config->link_address.sin6_addr, sizeof(in6_addr)); + memcpy(current_buffer_position, &new_message, sizeof(dhcpv6_relay_msg)); + current_buffer_position += sizeof(dhcpv6_relay_msg); + + if(config->is_option_79) { + linklayer_addr_option option79; + option79.link_layer_type = htons(1); + option79.option_code = htons(OPTION_CLIENT_LINKLAYER_ADDR); + option79.option_length = htons(2 + 6); // link_layer_type field + address + + memcpy(current_buffer_position, &option79, sizeof(linklayer_addr_option)); + current_buffer_position += sizeof(linklayer_addr_option); + + memcpy(current_buffer_position, ðer_hdr->ether_shost, sizeof(ether_hdr->ether_shost)); + current_buffer_position += sizeof(ether_hdr->ether_shost); + } + + auto dhcp_message_length = len; + relay_forward(current_buffer_position, parse_dhcpv6_hdr(msg), dhcp_message_length); + current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option); + + for(auto server: config->servers_sock) { + send_udp(sock, buffer, server, current_buffer_position - buffer, config, new_message.msg_type); + } +} + +/** + * @code relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config) + * + * @brief construct a relay-forward message encapsulated relay-forward message + * + * @param sock L3 socket for sending data to servers + * @param msg pointer to dhcpv6 message header position + * @param len size of data received + * @param ip_hdr pointer to IPv6 header + * @param config pointer to the relay interface config + * + * @return none + */ +void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config) { + static uint8_t buffer[4096]; + dhcpv6_relay_msg new_message; + auto current_buffer_position = buffer; + auto dhcp_relay_header = parse_dhcpv6_relay(msg); + + if (dhcp_relay_header->hop_count >= HOP_LIMIT) + return; + + new_message.msg_type = DHCPv6_MESSAGE_TYPE_RELAY_FORW; + memcpy(&new_message.peer_address, &ip_hdr->ip6_src, sizeof(in6_addr)); + new_message.hop_count = dhcp_relay_header->hop_count + 1; + + memset(&new_message.link_address, 0, sizeof(in6_addr)); + + memcpy(current_buffer_position, &new_message, sizeof(dhcpv6_relay_msg)); + current_buffer_position += sizeof(dhcpv6_relay_msg); + + auto dhcp_message_length = len; + relay_forward(current_buffer_position, parse_dhcpv6_hdr(msg), dhcp_message_length); + current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option); + + for(auto server: config->servers_sock) { + send_udp(sock, buffer, server, current_buffer_position - buffer, config, new_message.msg_type); + } +} + +/** + * @code relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs); + * + * @brief relay and unwrap a relay-reply message + * + * @param sock L3 socket for sending data to servers + * @param msg pointer to dhcpv6 message header position + * @param len size of data received + * @param config relay interface config + * + * @return none + */ + void relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *config) { + static uint8_t buffer[4096]; + uint8_t type = 0; + struct sockaddr_in6 target_addr; + auto current_buffer_position = buffer; + auto current_position = msg; + const uint8_t *tmp = NULL; + auto dhcp_relay_header = parse_dhcpv6_relay(msg); + current_position += sizeof(struct dhcpv6_relay_msg); + + auto position = current_position + sizeof(struct dhcpv6_option); + auto dhcpv6msg = parse_dhcpv6_hdr(position); + + while ((current_position - msg) < len) { + auto option = parse_dhcpv6_opt(current_position, &tmp); + current_position = tmp; + if (current_position - msg > len || ntohs(option->option_length) > sizeof(buffer) - (current_buffer_position - buffer)) { + break; + } + switch (ntohs(option->option_code)) { + case OPTION_RELAY_MSG: + memcpy(current_buffer_position, ((uint8_t *)option) + sizeof(struct dhcpv6_option), ntohs(option->option_length)); + current_buffer_position += ntohs(option->option_length); + type = dhcpv6msg->msg_type; + break; + default: + break; + } + } + + memcpy(&target_addr.sin6_addr, &dhcp_relay_header->peer_address, sizeof(struct in6_addr)); + target_addr.sin6_family = AF_INET6; + target_addr.sin6_flowinfo = 0; + target_addr.sin6_port = htons(CLIENT_PORT); + target_addr.sin6_scope_id = if_nametoindex(config->interface.c_str()); + + send_udp(sock, buffer, target_addr, current_buffer_position - buffer, config, type); +} + + +/** + * @code callback(evutil_socket_t fd, short event, void *arg); + * + * @brief callback for libevent that is called everytime data is received at the filter socket + * + * @param fd filter socket + * @param event libevent triggered event + * @param arg callback argument provided by user + * + * @return none + */ +void callback(evutil_socket_t fd, short event, void *arg) { + struct relay_config *config = (struct relay_config *)(arg); + static uint8_t message_buffer[4096]; + int32_t len = recv(config->filter, message_buffer, 4096, 0); + if (len <= 0) { + syslog(LOG_WARNING, "recv: Failed to receive data at filter socket: %s\n", strerror(errno)); + return; + } + + char* ptr = (char *)message_buffer; + const uint8_t *current_position = (uint8_t *)ptr; + const uint8_t *tmp = NULL; + const uint8_t *prev = NULL; + + auto ether_header = parse_ether_frame(current_position, &tmp); + current_position = tmp; + + auto ip_header = parse_ip6_hdr(current_position, &tmp); + current_position = tmp; + + prev = current_position; + if (ip_header->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_UDP) { + const struct ip6_ext *ext_header; + do { + ext_header = (const struct ip6_ext *)current_position; + current_position += ext_header->ip6e_len; + if((current_position == prev) || (current_position >= (uint8_t *)ptr + sizeof(message_buffer))) { + return; + } + prev = current_position; + } + while (ext_header->ip6e_nxt != IPPROTO_UDP); + } + + auto udp_header = parse_udp(current_position, &tmp); + current_position = tmp; + + auto msg = parse_dhcpv6_hdr(current_position); + counters[msg->msg_type]++; + std::string counterVlan = counter_table; + update_counter(counterVlan.append(config->interface), msg->msg_type); + + switch (msg->msg_type) { + case DHCPv6_MESSAGE_TYPE_RELAY_FORW: + { + relay_relay_forw(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, config); + break; + } + default: + { + relay_client(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, ether_header, config); + break; + } + } +} + +/** + * @code void server_callback(evutil_socket_t fd, short event, void *arg); + * + * @brief callback for libevent that is called everytime data is received at the server socket + * + * @param fd filter socket + * @param event libevent triggered event + * @param arg callback argument provided by user + * + * @return none + */ +void server_callback(evutil_socket_t fd, short event, void *arg) { + struct relay_config *config = (struct relay_config *)arg; + sockaddr_in6 from; + socklen_t len = sizeof(from); + int32_t data = 0; + static uint8_t message_buffer[4096]; + + if ((data = recvfrom(config->local_sock, message_buffer, 4096, 0, (sockaddr *)&from, &len)) == -1) { + syslog(LOG_WARNING, "recv: Failed to receive data from server\n"); + } + + auto msg = parse_dhcpv6_hdr(message_buffer); + + counters[msg->msg_type]++; + std::string counterVlan = counter_table; + update_counter(counterVlan.append(config->interface), msg->msg_type); + if (msg->msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) { + relay_relay_reply(config->server_sock, message_buffer, data, config); + } +} + +/** + * @code signal_init(); + * + * @brief initialize DHCPv6 Relay libevent signals + */ +int signal_init() { + int rv = -1; + do { + ev_sigint = evsignal_new(base, SIGINT, signal_callback, base); + if (ev_sigint == NULL) { + syslog(LOG_ERR, "Could not create SIGINT libevent signal\n"); + break; + } + + ev_sigterm = evsignal_new(base, SIGTERM, signal_callback, base); + if (ev_sigterm == NULL) { + syslog(LOG_ERR, "Could not create SIGTERM libevent signal\n"); + break; + } + rv = 0; + } while(0); + return rv; +} + +/** + * @code signal_start(); + * + * @brief start DHCPv6 Relay libevent base and add signals + */ +int signal_start() +{ + int rv = -1; + do + { + if (evsignal_add(ev_sigint, NULL) != 0) { + syslog(LOG_ERR, "Could not add SIGINT libevent signal\n"); + break; + } + + if (evsignal_add(ev_sigterm, NULL) != 0) { + syslog(LOG_ERR, "Could not add SIGTERM libevent signal\n"); + break; + } + + if (event_base_dispatch(base) != 0) { + syslog(LOG_ERR, "Could not start libevent dispatching loop\n"); + } + + rv = 0; + } while (0); + + return rv; +} + +/** + * @code signal_callback(fd, event, arg); + * + * @brief signal handler for dhcp6relay. Initiate shutdown when signal is caught + * + * @param fd libevent socket + * @param event event triggered + * @param arg pointer to libevent base + * + * @return none + */ +void signal_callback(evutil_socket_t fd, short event, void *arg) +{ + syslog(LOG_ALERT, "Received signal: '%s'\n", strsignal(fd)); + if ((fd == SIGTERM) || (fd == SIGINT)) { + dhcp6relay_stop(); + } +} + +/** + * @code dhcp6relay_stop(); + * + * @brief stop DHCPv6 Relay libevent loop upon signal + */ +void dhcp6relay_stop() +{ + event_base_loopexit(base, NULL); +} + +/** + * @code loop_relay(std::vector *vlans); + * + * @brief main loop: configure sockets, create libevent base, start server listener thread + * + * @param vlans list of vlans retrieved from config_db + */ +void loop_relay(std::vector *vlans) { + std::vector sockets; + base = event_base_new(); + if(base == NULL) { + syslog(LOG_ERR, "libevent: Failed to create base\n"); + } + + for(relay_config &vlan : *vlans) { + relay_config *config = &vlan; + int filter = 0; + int local_sock = 0; + int server_sock = 0; + int index = if_nametoindex(config->interface.c_str()); + + std::string counterVlan = counter_table; + initialize_counter(counterVlan.append(config->interface)); + + filter = sock_open(index, ðer_relay_fprog); + prepare_socket(&local_sock, &server_sock, config, index); + + config->local_sock = local_sock; + config->server_sock = server_sock; + + sockets.push_back(filter); + sockets.push_back(local_sock); + sockets.push_back(server_sock); + + prepare_relay_config(config, &local_sock, filter); + + evutil_make_listen_socket_reuseable(filter); + evutil_make_socket_nonblocking(filter); + + evutil_make_listen_socket_reuseable(local_sock); + evutil_make_socket_nonblocking(local_sock); + + listen_event = event_new(base, filter, EV_READ|EV_PERSIST, callback, config); + server_listen_event = event_new(base, local_sock, EV_READ|EV_PERSIST, server_callback, config); + if (listen_event == NULL || server_listen_event == NULL) { + syslog(LOG_ERR, "libevent: Failed to create libevent\n"); + } + + event_add(listen_event, NULL); + event_add(server_listen_event, NULL); + } + + if((signal_init() == 0) && signal_start() == 0) { + shutdown(); + for(std::size_t i = 0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define PACKED __attribute__ ((packed)) + +#define RELAY_PORT 547 +#define CLIENT_PORT 546 +#define HOP_LIMIT 8 //HOP_LIMIT reduced from 32 to 8 as stated in RFC8415 + +#define lengthof(A) (sizeof (A) / sizeof (A)[0]) + +#define OPTION_RELAY_MSG 9 +#define OPTION_CLIENT_LINKLAYER_ADDR 79 + +/* DHCPv6 message types */ +typedef enum +{ + DHCPv6_MESSAGE_TYPE_UNKNOWN = 0, + DHCPv6_MESSAGE_TYPE_SOLICIT = 1, + DHCPv6_MESSAGE_TYPE_ADVERTISE = 2, + DHCPv6_MESSAGE_TYPE_REQUEST = 3, + DHCPv6_MESSAGE_TYPE_CONFIRM = 4, + DHCPv6_MESSAGE_TYPE_RENEW = 5, + DHCPv6_MESSAGE_TYPE_REBIND = 6, + DHCPv6_MESSAGE_TYPE_REPLY = 7, + DHCPv6_MESSAGE_TYPE_RELEASE = 8, + DHCPv6_MESSAGE_TYPE_DECLINE = 9, + DHCPv6_MESSAGE_TYPE_RELAY_FORW = 12, + DHCPv6_MESSAGE_TYPE_RELAY_REPL = 13, + + DHCPv6_MESSAGE_TYPE_COUNT +} dhcp_message_type_t; + +struct relay_config { + int local_sock; + int server_sock; + int filter; + sockaddr_in6 link_address; + std::string interface; + std::vector servers; + std::vector servers_sock; + bool is_option_79; +}; + + +/* DHCPv6 messages and options */ + +struct dhcpv6_msg { + uint8_t msg_type; +}; + +struct PACKED dhcpv6_relay_msg { + uint8_t msg_type; + uint8_t hop_count; + struct in6_addr link_address; + struct in6_addr peer_address; +}; + + +struct dhcpv6_option { + uint16_t option_code; + uint16_t option_length; +}; + +struct linklayer_addr_option { + uint16_t option_code; + uint16_t option_length; + uint16_t link_layer_type; +}; + +/** + * @code sock_open(int ifindex, const struct sock_fprog *fprog); + * + * @brief prepare L2 socket to attach to "udp and port 547" filter + * + * @param ifindex interface index + * @param fprog bpf filter "udp and port 547" + * + * @return socket descriptor + */ +int sock_open(int ifindex, const struct sock_fprog *fprog); + +/** + * @code prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index); + * + * @brief prepare L3 socket for sending + * + * @param local_sock pointer to socket binded to global address for relaying client message to server and listening for server message + * @param server_sock pointer to socket binded to link_local address for relaying server message to client + * @param index scope id of interface + * + * @return none + */ +void prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index); + +/** + * @code prepare_relay_config(relay_config *interface_config, int *local_sock, int filter); + * + * @brief prepare for specified relay interface config: server and link address + * + * @param interface_config pointer to relay config to be prepared + * @param local_sock L3 socket used for relaying messages + * @param filter socket attached with filter + * + * @return none + */ +void prepare_relay_config(relay_config *interface_config, int *local_sock, int filter); + +/** + * @code relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length); + * + * @brief embed the DHCPv6 message received into DHCPv6 relay forward message + * + * @param buffer pointer to buffer + * @param msg pointer to parsed DHCPv6 message + * @param msg_length length of DHCPv6 message + * + * @return none + */ +void relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length); + +/** + * @code relay_client(int sock, const uint8_t *msg, int32_t len, ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config); + * + * @brief construct relay-forward message + * + * @param sock L3 socket for sending data to servers + * @param msg pointer to dhcpv6 message header position + * @param len size of data received + * @param ip_hdr pointer to IPv6 header + * @param ether_hdr pointer to Ethernet header + * @param config pointer to the relay interface config + * + * @return none + */ +void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config); + +/** + * @code relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config) + * + * @brief construct a relay-forward message encapsulated relay-forward message + * + * @param sock L3 socket for sending data to servers + * @param msg pointer to dhcpv6 message header position + * @param len size of data received + * @param ip_hdr pointer to IPv6 header + * @param config pointer to the relay interface config + * + * @return none + */ +void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config); + +/** + * @code relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs); + * + * @brief relay and unwrap a relay-reply message + * + * @param sock L3 socket for sending data to servers + * @param msg pointer to dhcpv6 message header position + * @param len size of data received + * @param config relay interface config + * + * @return none + */ +void relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs); + +/** + * @code loop_relay(std::vector *vlans); + * + * @brief main loop: configure sockets, create libevent base, start server listener thread + * + * @param vlans list of vlans retrieved from config_db + */ +void loop_relay(std::vector *vlans); + +/** + * @code signal_init(); + * + * @brief initialize DHCPv6 Relay libevent signals + */ +int signal_init(); + +/** + * @code signal_start(); + * + * @brief start DHCPv6 Relay libevent base and add signals + */ +int signal_start(); + +/** + * @code dhcp6relay_stop(); + * + * @brief stop DHCPv6 Relay libevent loop upon signal + */ +void dhcp6relay_stop(); + +/** + * @code signal_callback(fd, event, arg); + * + * @brief signal handler for dhcp6relay. Initiate shutdown when signal is caught + * + * @param fd libevent socket + * @param event event triggered + * @param arg pointer to libevent base + * + * @return none + */ +void signal_callback(evutil_socket_t fd, short event, void *arg); + +/** + * @code shutdown(); + * + * @brief free signals and terminate threads + */ +void shutdown(); + +/** + * @code void initialize_counter(std::string counterVlan) + * + * @brief initialize the counter by each Vlan + * + * @param counterVlan counter table with interface name + * + * @return none + */ +void initialize_counter(std::string counterVlan); + +/** + * @code void update_counter(std::string CounterVlan, uint8_t msg_type); + * + * @brief update the counter in state_db with count of each DHCPv6 message type + * + * @param counterVlan counter table with interface name + * @param msg_type dhcpv6 message type to be updated in counter + * + * @return none + */ +void update_counter(std::string counterVlan, uint8_t msg_type); + +/* Helper functions */ + +/** + * @code std::string toString(uint16_t count); + * + * @brief convert uint16_t to string + * + * @param count count of messages in counter + * + * @return count in string + */ +std::string toString(uint16_t count); + +/** + * @code const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end); + * + * @brief parse through ethernet frame + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return ether_header end of ethernet header position + */ +const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end); + +/** + * @code const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end); + * + * @brief parse through ipv6 header + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return ip6_hdr end of ipv6 header position + */ +const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end); + +/** + * @code const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end); + * + * @brief parse through udp header + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return udphdr end of udp header position + */ +const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end); + +/** + * @code const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer); + * + * @brief parse through dhcpv6 header + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return dhcpv6_msg end of dhcpv6 header position + */ +const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer); + +/** + * @code const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer); + * + * @brief parse through dhcpv6 relay message + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return dhcpv6_relay_msg start of dhcpv6 relay message or end of dhcpv6 message type position + */ +const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer); + +/** + * @code const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end); + * + * @brief parse through dhcpv6 option + * + * @param *buffer message buffer + * @param **out_end pointer + * + * @return dhcpv6_option end of dhcpv6 message option + */ +const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end); + +/** + * @code void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type); + * + * @brief send udp packet + * + * @param *buffer message buffer + * @param sockaddr_in6 target target socket + * @param n length of message + * @param relay_config *config pointer to relay_config + * @param uint8_t msg_type message type of dhcpv6 option of relayed message + * + * @return dhcpv6_option end of dhcpv6 message option + */ +void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type); + diff --git a/src/dhcp6relay/src/subdir.mk b/src/dhcp6relay/src/subdir.mk new file mode 100644 index 000000000000..c6e784ef5504 --- /dev/null +++ b/src/dhcp6relay/src/subdir.mk @@ -0,0 +1,23 @@ +CC := g++ + +C_SRCS += \ +../src/relay.c \ +../src/configInterface.c \ +../src/main.c + +OBJS += \ +./src/relay.o \ +./src/configInterface.o \ +./src/main.o + +C_DEPS += \ +./src/relay.d \ +./src/configInterface.d \ +./src/main.d + +src/%.o: src/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: GCC C++ Compiler' + $(CC) -D__FILENAME__="$(subst src/,,$<)" $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" +\ @echo 'Finished building: $<' + @echo ' ' diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index 836149bcbfd0..9ad536ad7cae 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -355,6 +355,7 @@ def parse_dpg(dpg, hname): vlanintfs = child.find(str(QName(ns, "VlanInterfaces"))) vlans = {} vlan_members = {} + dhcp_relay_table = {} intf_vlan_mbr = defaultdict(list) for vintf in vlanintfs.findall(str(QName(ns, "VlanInterface"))): vlanid = vintf.find(str(QName(ns, "VlanID"))).text @@ -376,6 +377,7 @@ def parse_dpg(dpg, hname): vlan_members[(sonic_vlan_member_name, vmbr_list[i])] = {'tagging_mode': 'untagged'} vlan_attributes = {'vlanid': vlanid, 'members': vmbr_list } + dhcp_attributes = {} # If this VLAN requires a DHCP relay agent, it will contain a element # containing a list of DHCP server IPs @@ -390,14 +392,17 @@ def parse_dpg(dpg, hname): vintfdhcpservers = vintf_node.text vdhcpserver_list = vintfdhcpservers.split(';') vlan_attributes['dhcpv6_servers'] = vdhcpserver_list + dhcp_attributes['dhcpv6_servers'] = vdhcpserver_list sonic_vlan_name = "Vlan%s" % vlanid if sonic_vlan_name != vintfname: vlan_attributes['alias'] = vintfname vlans[sonic_vlan_name] = vlan_attributes + dhcp_relay_table[sonic_vlan_name] = dhcp_attributes + ''' dhcp = child.find(str(QName(ns, "Dhcp"))) - dhcp_table = {} + dhcp_relay_table = {} if dhcp is not None: for vintf in dhcp.findall(str(QName(ns, "VlanInterface"))): @@ -417,7 +422,8 @@ def parse_dpg(dpg, hname): elif option_linklayer_addr is not None and option_linklayer_addr.text == "false": dhcp_attributes['dhcpv6_option|rfc6939_support'] = "false" - dhcp_table[vintfname] = dhcp_attributes + dhcp_relay_table[vintfname] = dhcp_attributes + ''' acls = {} for aclintf in aclintfs.findall(str(QName(ns, "AclInterface"))): @@ -519,7 +525,7 @@ def parse_dpg(dpg, hname): except: print >> sys.stderr, "Warning: Ignoring Control Plane ACL %s without type" % aclname - return intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, dhcp_table, pcs, pc_members, acls, vni + return intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, dhcp_relay_table, pcs, pc_members, acls, vni return None, None, None, None, None, None, None, None, None, None def parse_host_loopback(dpg, hname): @@ -1008,7 +1014,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None): for child in root: if asic_name is None: if child.tag == str(QName(ns, "DpgDec")): - (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, dhcp_table, pcs, pc_members, acls, vni) = parse_dpg(child, hostname) + (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, dhcp_relay_table, pcs, pc_members, acls, vni) = parse_dpg(child, hostname) elif child.tag == str(QName(ns, "CpgDec")): (bgp_sessions, bgp_internal_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, hostname) elif child.tag == str(QName(ns, "PngDec")): @@ -1023,7 +1029,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None): (port_speeds_default, port_descriptions) = parse_deviceinfo(child, hwsku) else: if child.tag == str(QName(ns, "DpgDec")): - (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, dhcp_table, pcs, pc_members, acls, vni) = parse_dpg(child, asic_name) + (intfs, lo_intfs, mvrf, mgmt_intf, vlans, vlan_members, dhcp_relay_table, pcs, pc_members, acls, vni) = parse_dpg(child, asic_name) host_lo_intfs = parse_host_loopback(child, hostname) elif child.tag == str(QName(ns, "CpgDec")): (bgp_sessions, bgp_internal_sessions, bgp_asn, bgp_peers_with_range, bgp_monitors) = parse_cpg(child, asic_name, local_devices) @@ -1277,7 +1283,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None): results['SYSLOG_SERVER'] = dict((item, {}) for item in syslog_servers) results['DHCP_SERVER'] = dict((item, {}) for item in dhcp_servers) results['DHCPv6_SERVER'] = dict((item, {}) for item in dhcpv6_servers) - results['DHCP'] = dhcp_table + results['DHCP_RELAY'] = dhcp_relay_table results['NTP_SERVER'] = dict((item, {}) for item in ntp_servers) results['TACPLUS_SERVER'] = dict((item, {'priority': '1', 'tcp_port': '49'}) for item in tacacs_servers) results['ACL_TABLE'] = filter_acl_table_bindings(acls, neighbors, pcs, sub_role) diff --git a/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf b/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf index 47bdee4b00bc..27c409f45c14 100644 --- a/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf +++ b/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf @@ -39,10 +39,11 @@ stderr_logfile=syslog dependent_startup=true dependent_startup_wait_for=rsyslogd:running -[group:isc-dhcp-relay] -programs=isc-dhcp-relay-Vlan1000 +[group:dhcp-relay] +programs=isc-dhcpv4-relay-Vlan1000,dhcp6relay -[program:isc-dhcp-relay-Vlan1000] + +[program:isc-dhcpv4-relay-Vlan1000] command=/usr/sbin/dhcrelay -d -m discard -a %%h:%%p %%P --name-alias-map-file /tmp/port-name-alias-map.txt -id Vlan1000 -iu Vlan2000 -iu PortChannel02 -iu PortChannel03 -iu PortChannel04 -iu PortChannel01 192.0.0.1 192.0.0.2 priority=3 autostart=false @@ -53,6 +54,15 @@ dependent_startup=true dependent_startup_wait_for=start:exited +[program:dhcp6relay] +command=/usr/sbin/dhcp6relay + +priority=3 +autostart=false +autorestart=false +stdout_logfile=syslog +stderr_logfile=syslog + [group:dhcpmon] programs=dhcpmon-Vlan1000 @@ -64,7 +74,6 @@ autorestart=false stdout_logfile=syslog stderr_logfile=syslog dependent_startup=true -dependent_startup_wait_for=isc-dhcp-relay-Vlan1000:running - +dependent_startup_wait_for=isc-dhcpv4-relay-Vlan1000:running diff --git a/src/sonic-config-engine/tests/sample_output/wait_for_intf.sh b/src/sonic-config-engine/tests/sample_output/wait_for_intf.sh index 4852f6167bad..8ba15b1c8355 100644 --- a/src/sonic-config-engine/tests/sample_output/wait_for_intf.sh +++ b/src/sonic-config-engine/tests/sample_output/wait_for_intf.sh @@ -30,3 +30,7 @@ wait_until_iface_ready PortChannel03 10.0.0.60/31 wait_until_iface_ready PortChannel04 10.0.0.62/31 wait_until_iface_ready PortChannel01 10.0.0.56/31 +# Wait 10 seconds for the rest of interfaces to get added/populated. +# dhcrelay listens on each of the interfaces (in addition to the port +# channels and vlan interfaces) +sleep 10 diff --git a/src/sonic-config-engine/tests/simple-sample-graph-case.xml b/src/sonic-config-engine/tests/simple-sample-graph-case.xml index 48460e2c6da8..b4e58ddaf724 100644 --- a/src/sonic-config-engine/tests/simple-sample-graph-case.xml +++ b/src/sonic-config-engine/tests/simple-sample-graph-case.xml @@ -131,23 +131,21 @@ ab1 fortyGigE0/8 192.0.0.1;192.0.0.2 + fc02:2000::1;fc02:2000::2 1000 1000 192.168.0.0/27 - - - - Vlan1000 - fc02:2000::1;fc02:2000::2 - true - - Vlan2000 + ab2 + fortyGigE0/4 + 192.0.0.3;192.0.0.4 fc02:2000::3;fc02:2000::4 - false + 2000 + 2000 + 192.168.0.0/27 - + diff --git a/src/sonic-config-engine/tests/test_cfggen.py b/src/sonic-config-engine/tests/test_cfggen.py index 4782c7fc2814..5bbcb4cafa03 100644 --- a/src/sonic-config-engine/tests/test_cfggen.py +++ b/src/sonic-config-engine/tests/test_cfggen.py @@ -619,9 +619,9 @@ def test_show_run_interfaces(self): self.assertEqual(output, '') def test_minigraph_dhcp(self): - argument = '-m "' + self.sample_graph_simple_case + '" -p "' + self.port_config + '" -v DHCP' + argument = '-m "' + self.sample_graph_simple_case + '" -p "' + self.port_config + '" -v DHCP_RELAY' output = self.run_script(argument) self.assertEqual( output.strip(), - "{'Vlan1000': {'dhcpv6_option|rfc6939_support': 'true', 'dhcpv6_servers': ['fc02:2000::1', 'fc02:2000::2']}, 'Vlan2000': {'dhcpv6_option|rfc6939_support': 'false', 'dhcpv6_servers': ['fc02:2000::3', 'fc02:2000::4']}}" + "{'Vlan1000': {'dhcpv6_servers': ['fc02:2000::1', 'fc02:2000::2']}, 'Vlan2000': {'dhcpv6_servers': ['fc02:2000::3', 'fc02:2000::4']}}" ) \ No newline at end of file diff --git a/src/sonic-config-engine/tests/test_minigraph_case.py b/src/sonic-config-engine/tests/test_minigraph_case.py index 85114396fce7..8c4d638fa711 100644 --- a/src/sonic-config-engine/tests/test_minigraph_case.py +++ b/src/sonic-config-engine/tests/test_minigraph_case.py @@ -82,15 +82,39 @@ def test_minigraph_interfaces(self): def test_minigraph_vlans(self): argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v VLAN' output = self.run_script(argument) + expected = { + u'Vlan1000': { + u'alias': 'ab1', + u'dhcp_servers': [u'192.0.0.1', u'192.0.0.2'], + u'dhcpv6_servers': [u'fc02:2000::1', u'fc02:2000::2'], + u'vlanid': u'1000', + u'members': [u'Ethernet8'] + }, + 'Vlan2000': { + u'alias': 'ab2', + u'dhcp_servers': ['192.0.0.3', '192.0.0.4'], + u'members': ['Ethernet4'], + u'dhcpv6_servers': [u'fc02:2000::3', u'fc02:2000::4'], + u'vlanid': '2000' + } + } + self.assertEqual( utils.to_dict(output.strip()), - utils.to_dict("{'Vlan1000': {'alias': 'ab1', 'dhcp_servers': ['192.0.0.1', '192.0.0.2'], 'vlanid': '1000', 'members': ['Ethernet8'] }}") + expected ) def test_minigraph_vlan_members(self): argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v VLAN_MEMBER' output = self.run_script(argument) - self.assertEqual(output.strip(), "{('Vlan1000', 'Ethernet8'): {'tagging_mode': 'untagged'}}") + expected = { + 'Vlan1000|Ethernet8': {'tagging_mode': 'untagged'}, + 'Vlan2000|Ethernet4': {'tagging_mode': 'untagged'} + } + self.assertEqual( + utils.to_dict(output.strip()), + expected + ) def test_minigraph_vlan_interfaces(self): argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v "VLAN_INTERFACE.keys()"' @@ -192,25 +216,23 @@ def test_parse_device_desc_xml_mgmt_interface(self): self.assertTrue(ipaddress.IPAddress('fc00:1::1') == mgmt_intf[('eth0', 'FC00:1::32/64')]['gwaddr']) def test_dhcp_table(self): - argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v "DHCP"' + argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v "DHCP_RELAY"' expected = { - 'Vlan1000': { - 'dhcpv6_servers': [ - "fc02:2000::1", - "fc02:2000::2" - ], - 'dhcpv6_option|rfc6939_support': 'true' - }, - 'Vlan2000': { - 'dhcpv6_servers': [ - "fc02:2000::3", - "fc02:2000::4" - ], - 'dhcpv6_option|rfc6939_support': 'false' - } + 'Vlan1000': { + 'dhcpv6_servers': [ + "fc02:2000::1", + "fc02:2000::2" + ] + }, + 'Vlan2000': { + 'dhcpv6_servers': [ + "fc02:2000::3", + "fc02:2000::4" + ] + } } output = self.run_script(argument) self.assertEqual( - output.strip(), - "{'Vlan1000': {'dhcpv6_option|rfc6939_support': 'true', 'dhcpv6_servers': ['fc02:2000::1', 'fc02:2000::2']}, 'Vlan2000': {'dhcpv6_option|rfc6939_support': 'false', 'dhcpv6_servers': ['fc02:2000::3', 'fc02:2000::4']}}" + utils.to_dict(output.strip()), + expected ) \ No newline at end of file diff --git a/src/sonic-dhcp-relay b/src/sonic-dhcp-relay new file mode 160000 index 000000000000..e2cd5851ddd9 --- /dev/null +++ b/src/sonic-dhcp-relay @@ -0,0 +1 @@ +Subproject commit e2cd5851ddd9b6496b59b72e75aaba8e4d7f284d From a6c00c9417a37e7013c5fda2bae26abefe5aaecc Mon Sep 17 00:00:00 2001 From: kellyyeh Date: Fri, 16 Sep 2022 22:52:11 +0000 Subject: [PATCH 4/7] Add dhcprelay submodule --- .gitmodules | 4 + .../docker-dhcp-relay/dhcp-relay.monitors.j2 | 15 +- rules/dhcp6relay.mk | 12 - rules/dhcprelay.mk | 12 + rules/docker-dhcp-relay.mk | 4 +- sonic-dhcp-relay | 1 - src/dhcp6relay/.gitignore | 5 - src/dhcp6relay/Makefile | 42 - src/dhcp6relay/debian/changelog | 5 - src/dhcp6relay/debian/compat | 1 - src/dhcp6relay/debian/control | 27 - src/dhcp6relay/debian/rules | 12 - src/dhcp6relay/src/configInterface.cpp | 151 ---- src/dhcp6relay/src/configInterface.d | 44 - src/dhcp6relay/src/configInterface.h | 75 -- src/dhcp6relay/src/main.cpp | 17 - src/dhcp6relay/src/main.d | 44 - src/dhcp6relay/src/relay.cpp | 816 ------------------ src/dhcp6relay/src/relay.d | 51 -- src/dhcp6relay/src/relay.h | 349 -------- src/dhcp6relay/src/subdir.mk | 23 - src/dhcprelay | 1 + src/sonic-config-engine/minigraph.py | 27 +- .../tests/test_minigraph_case.py | 40 +- src/sonic-dhcp-relay | 1 - 25 files changed, 46 insertions(+), 1733 deletions(-) delete mode 100644 rules/dhcp6relay.mk create mode 100644 rules/dhcprelay.mk delete mode 160000 sonic-dhcp-relay delete mode 100644 src/dhcp6relay/.gitignore delete mode 100644 src/dhcp6relay/Makefile delete mode 100644 src/dhcp6relay/debian/changelog delete mode 100644 src/dhcp6relay/debian/compat delete mode 100644 src/dhcp6relay/debian/control delete mode 100755 src/dhcp6relay/debian/rules delete mode 100644 src/dhcp6relay/src/configInterface.cpp delete mode 100644 src/dhcp6relay/src/configInterface.d delete mode 100644 src/dhcp6relay/src/configInterface.h delete mode 100644 src/dhcp6relay/src/main.cpp delete mode 100644 src/dhcp6relay/src/main.d delete mode 100644 src/dhcp6relay/src/relay.cpp delete mode 100644 src/dhcp6relay/src/relay.d delete mode 100644 src/dhcp6relay/src/relay.h delete mode 100644 src/dhcp6relay/src/subdir.mk create mode 160000 src/dhcprelay delete mode 160000 src/sonic-dhcp-relay diff --git a/.gitmodules b/.gitmodules index 82e364085a88..0ade5f29a954 100644 --- a/.gitmodules +++ b/.gitmodules @@ -83,3 +83,7 @@ [submodule "src/sonic-ztp"] path = src/sonic-ztp url = https://github.com/Azure/sonic-ztp +[submodule "src/dhcprelay"] + path = src/dhcprelay + url = https://github.com/sonic-net/sonic-dhcp-relay.git + branch = 201911 diff --git a/dockers/docker-dhcp-relay/dhcp-relay.monitors.j2 b/dockers/docker-dhcp-relay/dhcp-relay.monitors.j2 index 0928bac22d4d..4c6438fc99f2 100644 --- a/dockers/docker-dhcp-relay/dhcp-relay.monitors.j2 +++ b/dockers/docker-dhcp-relay/dhcp-relay.monitors.j2 @@ -6,9 +6,6 @@ programs= {% if VLAN and vlan_name in VLAN and 'dhcp_servers' in VLAN[vlan_name] and VLAN[vlan_name]['dhcp_servers']|length > 0 %} {% set _dummy = monitor_instance.update({'flag': True}) %} {%- endif %} -{% if DHCP_RELAY and vlan_name in DHCP_RELAY and DHCP_RELAY[vlan_name]['dhcpv6_servers']|length > 0 %} -{% set _dummy = monitor_instance.update({'flag': True}) %} -{%- endif %} {% if monitor_instance.flag %} {% if add_preceding_comma.flag %},{% endif %} {% set _dummy = add_preceding_comma.update({'flag': True}) %} @@ -20,7 +17,6 @@ dhcpmon-{{ vlan_name }} {# Create a program entry for each DHCP MONitor instance #} {% set relay_for_ipv4 = { 'flag': False } %} -{% set relay_for_ipv6 = { 'flag': False } %} {% for vlan_name in VLAN_INTERFACE %} {# Check DHCPv4 agents #} {% if VLAN and vlan_name in VLAN and 'dhcp_servers' in VLAN[vlan_name] and VLAN[vlan_name]['dhcp_servers']|length > 0 %} @@ -30,15 +26,7 @@ dhcpmon-{{ vlan_name }} {% endif %} {% endfor %} {% endif %} -{# Check DHCPv6 agents #} -{% if DHCP_RELAY and vlan_name in DHCP_RELAY and DHCP_RELAY[vlan_name]['dhcpv6_servers']|length > 0 %} -{% for dhcpv6_server in VLAN[vlan_name]['dhcpv6_servers'] %} -{% if dhcpv6_server | ipv6 %} -{% set _dummy = relay_for_ipv6.update({'flag': True}) %} -{% endif %} -{% endfor %} -{% endif %} -{% if relay_for_ipv4.flag or relay_for_ipv6.flag %} +{% if relay_for_ipv4.flag %} [program:dhcpmon-{{ vlan_name }}] {# We treat this VLAN as a downstream interface (-id), as we only want to listen for requests #} command=/usr/sbin/dhcpmon -id {{ vlan_name }} @@ -71,6 +59,5 @@ dependent_startup_wait_for= {% set _dummy = relay_for_ipv4.update({'flag': False}) %} -{% set _dummy = relay_for_ipv6.update({'flag': False}) %} {% endif %} {% endfor %} diff --git a/rules/dhcp6relay.mk b/rules/dhcp6relay.mk deleted file mode 100644 index 7f1ab3a1e354..000000000000 --- a/rules/dhcp6relay.mk +++ /dev/null @@ -1,12 +0,0 @@ -# SONiC DHCPV6 RELAY Package - -SONIC_DHCP6RELAY_VERSION = 1.0.0-0 -SONIC_DHCP6RELAY_PKG_NAME = dhcp6relay - -SONIC_DHCP6RELAY = sonic-$(SONIC_DHCP6RELAY_PKG_NAME)_$(SONIC_DHCP6RELAY_VERSION)_$(CONFIGURED_ARCH).deb -$(SONIC_DHCP6RELAY)_DEPENDS = $(LIBSWSSCOMMON) $(LIBHIREDIS) $(LIBSWSSCOMMON_DEV) $(LIBHIREDIS_DEV) -$(SONIC_DHCP6RELAY)_SRC_PATH = $(SRC_PATH)/$(SONIC_DHCP6RELAY_PKG_NAME) -SONIC_DPKG_DEBS += $(SONIC_DHCP6RELAY) - -SONIC_DHCP6RELAY_DBG = sonic-$(SONIC_DHCP6RELAY_PKG_NAME)-dbg_$(SONIC_DHCP6RELAY_VERSION)_amd64.deb -$(eval $(call add_derived_package,$(SONIC_DHCP6RELAY),$(SONIC_DHCP6RELAY_DBG))) diff --git a/rules/dhcprelay.mk b/rules/dhcprelay.mk new file mode 100644 index 000000000000..5129c3bcbe31 --- /dev/null +++ b/rules/dhcprelay.mk @@ -0,0 +1,12 @@ +# SONiC DHCPV6 RELAY Package + +SONIC_DHCPRELAY_VERSION = 1.0.0-0 +SONIC_DHCPRELAY_PKG_NAME = dhcp6relay + +SONIC_DHCPRELAY = sonic-$(SONIC_DHCPRELAY_PKG_NAME)_$(SONIC_DHCPRELAY_VERSION)_$(CONFIGURED_ARCH).deb +$(SONIC_DHCPRELAY)_DEPENDS = $(LIBSWSSCOMMON) $(LIBHIREDIS) $(LIBSWSSCOMMON_DEV) $(LIBHIREDIS_DEV) +$(SONIC_DHCPRELAY)_SRC_PATH = $(SRC_PATH)/dhcprelay +SONIC_DPKG_DEBS += $(SONIC_DHCPRELAY) + +SONIC_DHCPRELAY_DBG = sonic-$(SONIC_DHCPRELAY_PKG_NAME)-dbgsym_$(SONIC_DHCPRELAY_VERSION)_amd64.deb +$(eval $(call add_derived_package,$(SONIC_DHCPRELAY),$(SONIC_DHCPRELAY_DBG))) diff --git a/rules/docker-dhcp-relay.mk b/rules/docker-dhcp-relay.mk index 4a724443fb88..4182d3ec137f 100644 --- a/rules/docker-dhcp-relay.mk +++ b/rules/docker-dhcp-relay.mk @@ -6,9 +6,9 @@ DOCKER_DHCP_RELAY_DBG = $(DOCKER_DHCP_RELAY_STEM)-$(DBG_IMAGE_MARK).gz $(DOCKER_DHCP_RELAY)_PATH = $(DOCKERS_PATH)/$(DOCKER_DHCP_RELAY_STEM) -$(DOCKER_DHCP_RELAY)_DEPENDS += $(ISC_DHCP_RELAY) $(REDIS_TOOLS) $(SONIC_DHCPMON) $(SONIC_DHCP6RELAY) $(LIBSWSSCOMMON) +$(DOCKER_DHCP_RELAY)_DEPENDS += $(ISC_DHCP_RELAY) $(REDIS_TOOLS) $(SONIC_DHCPMON) $(SONIC_DHCPRELAY) $(LIBSWSSCOMMON) $(DOCKER_DHCP_RELAY)_DBG_DEPENDS = $($(DOCKER_CONFIG_ENGINE_STRETCH)_DBG_DEPENDS) -$(DOCKER_DHCP_RELAY)_DBG_DEPENDS += $(ISC_DHCP_RELAY_DBG) $(SONIC_DHCP6RELAY_DBG) +$(DOCKER_DHCP_RELAY)_DBG_DEPENDS += $(ISC_DHCP_RELAY_DBG) $(SONIC_DHCPRELAY_DBG) $(DOCKER_DHCP_RELAY)_DBG_IMAGE_PACKAGES = $($(DOCKER_CONFIG_ENGINE_STRETCH)_DBG_IMAGE_PACKAGES) diff --git a/sonic-dhcp-relay b/sonic-dhcp-relay deleted file mode 160000 index e2cd5851ddd9..000000000000 --- a/sonic-dhcp-relay +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e2cd5851ddd9b6496b59b72e75aaba8e4d7f284d diff --git a/src/dhcp6relay/.gitignore b/src/dhcp6relay/.gitignore deleted file mode 100644 index 9d09ae6b3f1a..000000000000 --- a/src/dhcp6relay/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -debian/* -!debian/changelog -!debian/compat -!debian/control -!debian/rules diff --git a/src/dhcp6relay/Makefile b/src/dhcp6relay/Makefile deleted file mode 100644 index 8b5740fd787e..000000000000 --- a/src/dhcp6relay/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -RM := rm -rf -DHCP6RELAY_TARGET := dhcp6relay -CP := cp -MKDIR := mkdir -CC := g++ -MV := mv -LIBS := -levent -lhiredis -lswsscommon -pthread -lboost_thread -lboost_system -CFLAGS += -Wall -std=c++14 -fPIE -I$(PWD)/../sonic-swss-common/common -PWD := $(shell pwd) - -ifneq ($(MAKECMDGOALS),clean) -ifneq ($(strip $(C_DEPS)),) --include $(C_DEPS) $(OBJS) -endif -endif - --include src/subdir.mk - -all: sonic-dhcp6relay - -sonic-dhcp6relay: $(OBJS) - @echo 'Building target: $@' - @echo 'Invoking: G++ Linker' - $(CC) $(LDFLAGS) -o $(DHCP6RELAY_TARGET) $(OBJS) $(LIBS) - @echo 'Finished building target: $@' - @echo ' ' - -install: - $(MKDIR) -p $(DESTDIR)/usr/sbin - $(MV) $(DHCP6RELAY_TARGET) $(DESTDIR)/usr/sbin - -deinstall: - $(RM) $(DESTDIR)/usr/sbin/$(DHCP6RELAY_TARGET) - $(RM) -rf $(DESTDIR)/usr/sbin - -clean: - -$(RM) $(EXECUTABLES) $(C_DEPS) $(OBJS) $(DHCP6RELAY_TARGET) - -@echo ' ' - -.PHONY: all clean dependents - - diff --git a/src/dhcp6relay/debian/changelog b/src/dhcp6relay/debian/changelog deleted file mode 100644 index aec73a095801..000000000000 --- a/src/dhcp6relay/debian/changelog +++ /dev/null @@ -1,5 +0,0 @@ -sonic-dhcp6relay (1.0.0-0) UNRELEASED; urgency=medium - - * Initial release. - - -- Kelly Yeh Fri, 15 Oct 2021 00:00:00 +1000 diff --git a/src/dhcp6relay/debian/compat b/src/dhcp6relay/debian/compat deleted file mode 100644 index ec635144f600..000000000000 --- a/src/dhcp6relay/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/src/dhcp6relay/debian/control b/src/dhcp6relay/debian/control deleted file mode 100644 index fb82be7c911f..000000000000 --- a/src/dhcp6relay/debian/control +++ /dev/null @@ -1,27 +0,0 @@ -Source: sonic-dhcp6relay -Section: devel -Priority: optional -Maintainer: Kelly Yeh -Build-Depends: debhelper (>= 8.0.0), - dh-systemd -Standards-Version: 3.9.3 -Homepage: https://github.com/Azure/sonic-buildimage -XS-Go-Import-Path: github.com/Azure/sonic-buildimage - -Package: sonic-dhcp6relay -Architecture: any -Built-Using: ${misc:Built-Using} -Depends: libevent-2.0-5, - libboost-thread1.55.0, - libboost-system1.55.0 -Description: SONiC DHCPv6 Relay - -Package: sonic-dhcp6relay-dbg -Architecture: any -Section: debug -Priority: extra -Built-Using: ${misc:Built-Using} -Depends: libevent-2.0-5, - libboost-thread1.55.0, - libboost-system1.55.0 -Description: SONiC DHCPv6 Relay debug symbols diff --git a/src/dhcp6relay/debian/rules b/src/dhcp6relay/debian/rules deleted file mode 100755 index ed8a2397a6d4..000000000000 --- a/src/dhcp6relay/debian/rules +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/make -f - -export DEB_BUILD_MAINT_OPTIONS=hardening=+all - -%: - dh $@ --parallel - -override_dh_strip: - dh_strip --dbg-package=sonic-dhcp6relay-dbg - -override_dh_auto_install: - dh_auto_install --destdir=debian/sonic-dhcp6relay diff --git a/src/dhcp6relay/src/configInterface.cpp b/src/dhcp6relay/src/configInterface.cpp deleted file mode 100644 index dfe6031e0468..000000000000 --- a/src/dhcp6relay/src/configInterface.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include -#include -#include "configInterface.h" - -constexpr auto DEFAULT_TIMEOUT_MSEC = 1000; - -bool pollSwssNotifcation = true; -std::shared_ptr mSwssThreadPtr; - -std::shared_ptr configDbPtr = std::make_shared (4, "localhost", 6379, 0); -swss::SubscriberStateTable ipHelpersTable(configDbPtr.get(), "DHCP_RELAY"); -swss::Select swssSelect; - -/** - * @code void initialize_swss() - * - * @brief initialize DB tables and start SWSS listening thread - * - * @return none - */ -void initialize_swss(std::vector *vlans) -{ - try { - swssSelect.addSelectable(&ipHelpersTable); - get_dhcp(vlans); - mSwssThreadPtr = std::make_shared (&handleSwssNotification, vlans); - } - catch (const std::bad_alloc &e) { - syslog(LOG_ERR, "Failed allocate memory. Exception details: %s", e.what()); - } -} - -/** - * @code void deinitialize_swss() - * - * @brief deinitialize DB interface and join SWSS listening thread - * - * @return none - */ -void deinitialize_swss() -{ - stopSwssNotificationPoll(); - mSwssThreadPtr->interrupt(); -} - - -/** - * @code void get_dhcp(std::vector *vlans) - * - * @brief initialize and get vlan table information from DHCP_RELAY - * - * @return none - */ -void get_dhcp(std::vector *vlans) { - swss::Selectable *selectable; - int ret = swssSelect.select(&selectable, DEFAULT_TIMEOUT_MSEC); - if (ret == swss::Select::ERROR) { - syslog(LOG_WARNING, "Select: returned ERROR"); - } else if (ret == swss::Select::TIMEOUT) { - } - if (selectable == static_cast (&ipHelpersTable)) { - handleRelayNotification(ipHelpersTable, vlans); - } -} -/** - * @code void handleSwssNotification(std::vector *vlans) - * - * @brief main thread for handling SWSS notification - * - * @param context list of vlans/argument config that contains strings of server and option - * - * @return none - */ -void handleSwssNotification(std::vector *vlans) -{ - while (pollSwssNotifcation) { - get_dhcp(vlans); - } -} - -/** - * @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::vector *vlans) - * - * @brief handles DHCPv6 relay configuration change notification - * - * @param ipHelpersTable DHCP table - * @param vlans list of vlans/argument config that contains strings of server and option - * - * @return none - */ -void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::vector *vlans) -{ - std::deque entries; - - ipHelpersTable.pops(entries); - processRelayNotification(entries, vlans); -} - -/** - * @code void processRelayNotification(std::deque &entries, std::vector *vlans) - * - * @brief process DHCPv6 relay servers and options configuration change notification - * - * @param entries queue of std::tuple> entries in DHCP table - * @param vlans list of vlans/argument config that contains strings of server and option - * - * @return none - */ -void processRelayNotification(std::deque &entries, std::vector *vlans) -{ - std::vector servers; - - for (auto &entry: entries) { - std::string vlan = kfvKey(entry); - std::string operation = kfvOp(entry); - std::vector fieldValues = kfvFieldsValues(entry); - - relay_config intf; - intf.is_option_79 = true; - intf.interface = vlan; - for (auto &fieldValue: fieldValues) { - std::string f = fvField(fieldValue); - std::string v = fvValue(fieldValue); - if(f == "dhcpv6_servers") { - std::stringstream ss(v); - while (ss.good()) { - std::string substr; - getline(ss, substr, ','); - intf.servers.push_back(substr); - } - syslog(LOG_DEBUG, "key: %s, Operation: %s, f: %s, v: %s", vlan.c_str(), operation.c_str(), f.c_str(), v.c_str()); - } - if(f == "dhcpv6_option|rfc6939_support" && v == "false") { - intf.is_option_79 = false; - } - } - vlans->push_back(intf); - } -} - -/** -*@code stopSwssNotificationPoll -* -*@brief stop SWSS listening thread -* -*@return none -*/ -void stopSwssNotificationPoll() { - pollSwssNotifcation = false; -}; diff --git a/src/dhcp6relay/src/configInterface.d b/src/dhcp6relay/src/configInterface.d deleted file mode 100644 index d803bf1a54de..000000000000 --- a/src/dhcp6relay/src/configInterface.d +++ /dev/null @@ -1,44 +0,0 @@ -src/configInterface.o: src/configInterface.cpp src/configInterface.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/subscriberstatetable.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/dbconnector.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/consumertablebase.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/table.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/redisreply.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/rediscommand.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/redisselect.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/selectable.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/redispipeline.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/schema.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/redistran.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/logger.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/select.h src/relay.h - -src/configInterface.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/subscriberstatetable.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/dbconnector.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/consumertablebase.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/table.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/redisreply.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/rediscommand.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/redisselect.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/selectable.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/redispipeline.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/schema.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/redistran.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/logger.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/select.h: - -src/relay.h: diff --git a/src/dhcp6relay/src/configInterface.h b/src/dhcp6relay/src/configInterface.h deleted file mode 100644 index 20b0912c5cef..000000000000 --- a/src/dhcp6relay/src/configInterface.h +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include "subscriberstatetable.h" -#include "select.h" -#include "relay.h" - -/** - * @code void initialize_swss() - * - * @brief initialize DB tables and start SWSS listening thread - * - * @return none - */ -void initialize_swss(std::vector *vlans); - -/** - * @code void deinitialize_swss() - * - * @brief deinitialize DB interface and join SWSS listening thread - * - * @return none - */ -void deinitialize_swss(); - -/** - * @code void get_dhcp(std::vector *vlans) - * - * @brief initialize and get vlan information from DHCP_RELAY - * - * @return none - */ -void get_dhcp(std::vector *vlans); - -/** - * @code void handleSwssNotification(std::vector *vlans) - * - * @brief main thread for handling SWSS notification - * - * @param vlans list of vlans/argument config that contains strings of server and option - * - * @return none - */ -void handleSwssNotification(std::vector *vlans); - -/** - * @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::vector *vlans) - * - * @brief handles DHCPv6 relay configuration change notification - * - * @param ipHelpersTable DHCP table - * @param vlans list of vlans/argument config that contains strings of server and option - * - * @return none - */ -void handleRelayNotification(swss::SubscriberStateTable &configMuxTable, std::vector *vlans); - -/** - * @code void processRelayNotification(std::deque &entries, std::vector *vlans) - * - * @brief process DHCPv6 relay servers and options configuration change notification - * - * @param entries queue of std::tuple> entries in DHCP table - * @param context list of vlans/argument config that contains strings of server and option - * - * @return none - */ -void processRelayNotification(std::deque &entries, std::vector *vlans); - -/** -*@code stopSwssNotificationPoll -* -*@brief stop SWSS listening thread -* -*@return none -*/ -void stopSwssNotificationPoll(); diff --git a/src/dhcp6relay/src/main.cpp b/src/dhcp6relay/src/main.cpp deleted file mode 100644 index 6d0fb29c050f..000000000000 --- a/src/dhcp6relay/src/main.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include -#include "configInterface.h" - -int main(int argc, char *argv[]) { - try { - std::vector vlans; - initialize_swss(&vlans); - loop_relay(&vlans); - } - catch (std::exception &e) - { - syslog(LOG_ERR, "An exception occurred.\n"); - return 1; - } - return 0; -} diff --git a/src/dhcp6relay/src/main.d b/src/dhcp6relay/src/main.d deleted file mode 100644 index 05e7fd2367f0..000000000000 --- a/src/dhcp6relay/src/main.d +++ /dev/null @@ -1,44 +0,0 @@ -src/main.o: src/main.cpp src/configInterface.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/subscriberstatetable.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/dbconnector.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/consumertablebase.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/table.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/redisreply.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/rediscommand.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/redisselect.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/selectable.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/redispipeline.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/schema.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/redistran.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/logger.h \ - /sonic/src/dhcp6relay/../sonic-swss-common/common/select.h src/relay.h - -src/configInterface.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/subscriberstatetable.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/dbconnector.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/consumertablebase.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/table.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/redisreply.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/rediscommand.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/redisselect.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/selectable.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/redispipeline.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/schema.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/redistran.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/logger.h: - -/sonic/src/dhcp6relay/../sonic-swss-common/common/select.h: - -src/relay.h: diff --git a/src/dhcp6relay/src/relay.cpp b/src/dhcp6relay/src/relay.cpp deleted file mode 100644 index c4589774bfa9..000000000000 --- a/src/dhcp6relay/src/relay.cpp +++ /dev/null @@ -1,816 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dbconnector.h" -#include "configInterface.h" -#include "redisclient.h" - - -struct event *listen_event; -struct event *server_listen_event; -struct event_base *base; -struct event *ev_sigint; -struct event *ev_sigterm; -static std::string counter_table = "DHCPv6_COUNTER_TABLE|"; -swss::DBConnector state_db(6, "localhost", 6379, 0); -swss::RedisClient m_stateDbRedisClient(&state_db); - -/* DHCPv6 filter */ -/* sudo tcpdump -dd "ip6 dst ff02::1:2 && udp dst port 547" */ - -static struct sock_filter ether_relay_filter[] = { - - { 0x28, 0, 0, 0x0000000c }, - { 0x15, 0, 13, 0x000086dd }, - { 0x20, 0, 0, 0x00000026 }, - { 0x15, 0, 11, 0xff020000 }, - { 0x20, 0, 0, 0x0000002a }, - { 0x15, 0, 9, 0x00000000 }, - { 0x20, 0, 0, 0x0000002e }, - { 0x15, 0, 7, 0x00000000 }, - { 0x20, 0, 0, 0x00000032 }, - { 0x15, 0, 5, 0x00010002 }, - { 0x30, 0, 0, 0x00000014 }, - { 0x15, 0, 3, 0x00000011 }, - { 0x28, 0, 0, 0x00000038 }, - { 0x15, 0, 1, 0x00000223 }, - { 0x6, 0, 0, 0x00040000 }, - { 0x6, 0, 0, 0x00000000 }, -}; -const struct sock_fprog ether_relay_fprog = { - lengthof(ether_relay_filter), - ether_relay_filter -}; - -/* DHCPv6 Counter */ -uint64_t counters[DHCPv6_MESSAGE_TYPE_COUNT]; -std::map counterMap = {{0, "Unknown"}, - {1, "Solicit"}, - {2, "Advertise"}, - {3, "Request"}, - {4, "Confirm"}, - {5, "Renew"}, - {6, "Rebind"}, - {7, "Reply"}, - {8, "Release"}, - {9, "Decline"}, - {12, "Relay-Forward"}, - {13, "Relay-Reply"}}; - -/** - * @code void initialize_counter(std::string counterVlan); - * - * @brief initialize the counter by each Vlan - * - * @param counterVlan counter table with interface name - * - * @return none - */ -void initialize_counter(std::string counterVlan) { - m_stateDbRedisClient.hset(counterVlan, "Unknown", toString(counters[DHCPv6_MESSAGE_TYPE_UNKNOWN])); - m_stateDbRedisClient.hset(counterVlan, "Solicit", toString(counters[DHCPv6_MESSAGE_TYPE_SOLICIT])); - m_stateDbRedisClient.hset(counterVlan, "Advertise", toString(counters[DHCPv6_MESSAGE_TYPE_ADVERTISE])); - m_stateDbRedisClient.hset(counterVlan, "Request", toString(counters[DHCPv6_MESSAGE_TYPE_REQUEST])); - m_stateDbRedisClient.hset(counterVlan, "Confirm", toString(counters[DHCPv6_MESSAGE_TYPE_CONFIRM])); - m_stateDbRedisClient.hset(counterVlan, "Renew", toString(counters[DHCPv6_MESSAGE_TYPE_RENEW])); - m_stateDbRedisClient.hset(counterVlan, "Rebind", toString(counters[DHCPv6_MESSAGE_TYPE_REBIND])); - m_stateDbRedisClient.hset(counterVlan, "Reply", toString(counters[DHCPv6_MESSAGE_TYPE_REPLY])); - m_stateDbRedisClient.hset(counterVlan, "Release", toString(counters[DHCPv6_MESSAGE_TYPE_RELEASE])); - m_stateDbRedisClient.hset(counterVlan, "Decline", toString(counters[DHCPv6_MESSAGE_TYPE_DECLINE])); - m_stateDbRedisClient.hset(counterVlan, "Relay-Forward", toString(counters[DHCPv6_MESSAGE_TYPE_RELAY_FORW])); - m_stateDbRedisClient.hset(counterVlan, "Relay-Reply", toString(counters[DHCPv6_MESSAGE_TYPE_RELAY_REPL])); -} - -/** - * @code void update_counter(std::string CounterVlan, uint8_t msg_type); - * - * @brief update the counter in state_db with count of each DHCPv6 message type - * - * @param counterVlan counter table with interface name - * @param msg_type dhcpv6 message type to be updated in counter - * - * @return none - */ -void update_counter(std::string counterVlan, uint8_t msg_type) { - m_stateDbRedisClient.hset(counterVlan, counterMap.find(msg_type)->second, toString(counters[msg_type])); -} - -/** - * @code std::string toString(uint16_t count); - * - * @brief convert uint16_t to string - * - * @param count count of messages in counter - * - * @return count in string - */ -std::string toString(uint16_t count) { - std::stringstream ss; - ss << count; - std::string countValue = ss.str(); - return countValue; -} - -/** - * @code const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end); - * - * @brief parse through ethernet frame - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return ether_header end of ethernet header position - */ -const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end) { - (*out_end) = buffer + sizeof(struct ether_header); - return (const struct ether_header *)buffer; -} - -/** - * @code const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end); - * - * @brief parse through ipv6 header - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return ip6_hdr end of ipv6 header position - */ -const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end) { - (*out_end) = buffer + sizeof(struct ip6_hdr); - return (struct ip6_hdr *)buffer; -} - -/** - * @code const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end); - * - * @brief parse through udp header - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return udphdr end of udp header position - */ -const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end) { - (*out_end) = buffer + sizeof(struct udphdr); - return (const struct udphdr *)buffer; -} - -/** - * @code const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer); - * - * @brief parse through dhcpv6 header - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return dhcpv6_msg end of dhcpv6 header position - */ -const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer) { - return (const struct dhcpv6_msg *)buffer; -} - -/** - * @code const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer); - * - * @brief parse through dhcpv6 relay message - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return dhcpv6_relay_msg start of dhcpv6 relay message or end of dhcpv6 message type position - */ -const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer) { - return (const struct dhcpv6_relay_msg *)buffer; -} - -/** - * @code const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end); - * - * @brief parse through dhcpv6 option - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return dhcpv6_option end of dhcpv6 message option - */ -const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end) { - uint32_t size = 4; // option-code + option-len - size += ntohs(*(uint16_t *)(buffer + 2)); - (*out_end) = buffer + size; - - return (const struct dhcpv6_option *)buffer; -} - -/** - * @code void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type); - * - * @brief send udp packet - * - * @param *buffer message buffer - * @param sockaddr_in6 target target socket - * @param n length of message - * @param relay_config *config pointer to relay_config - * @param uint8_t msg_type message type of dhcpv6 option of relayed message - * - * @return dhcpv6_option end of dhcpv6 message option - */ -void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type) { - std::string counterVlan = counter_table; - if(sendto(sock, buffer, n, 0, (const struct sockaddr *)&target, sizeof(target)) == -1) - syslog(LOG_ERR, "sendto: Failed to send to target address\n"); - else if (counterMap.find(msg_type) != counterMap.end()) { - counters[msg_type]++; - update_counter(counterVlan.append(config->interface), msg_type); - } else { - syslog(LOG_WARNING, "unexpected message type %d(0x%x)\n", msg_type, msg_type); - } -} - -/** - * @code relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length); - * - * @brief embed the DHCPv6 message received into DHCPv6 relay forward message - * - * @param buffer pointer to buffer - * @param msg pointer to parsed DHCPv6 message - * @param msg_length length of DHCPv6 message - * - * @return none - */ -void relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length) { - struct dhcpv6_option option; - option.option_code = htons(OPTION_RELAY_MSG); - option.option_length = htons(msg_length); - memcpy(buffer, &option, sizeof(struct dhcpv6_option)); - memcpy(buffer + sizeof(struct dhcpv6_option), msg, msg_length); -} - -/** - * @code sock_open(int ifindex, const struct sock_fprog *fprog); - * - * @brief prepare L2 socket to attach to "udp and port 547" filter - * - * @param ifindex interface index - * @param fprog bpf filter "udp and port 547" - * - * @return socket descriptor - */ -int sock_open(int ifindex, const struct sock_fprog *fprog) -{ - if (!ifindex) { - errno = EINVAL; - return -1; - } - - int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (s == -1) { - syslog(LOG_ERR, "socket: Failed to create socket\n"); - return -1; - } - - struct sockaddr_ll sll = { - .sll_family = AF_PACKET, - .sll_protocol = htons(ETH_P_ALL), - .sll_ifindex = ifindex - }; - - if (bind(s, (struct sockaddr *)&sll, sizeof sll) == -1) { - syslog(LOG_ERR, "bind: Failed to bind to specified interface\n"); - (void) close(s); - return -1; - } - - if (fprog && setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, fprog, sizeof *fprog) == -1) - { - syslog(LOG_ERR, "setsockopt: Failed to attach filter\n"); - (void) close(s); - return -1; - } - - return s; -} - -/** - * @code prepare_relay_config(relay_config *interface_config, int local_sock, int filter); - * - * @brief prepare for specified relay interface config: server and link address - * - * @param interface_config pointer to relay config to be prepared - * @param local_sock L3 socket used for relaying messages - * @param filter socket attached with filter - * - * @return none - */ -void prepare_relay_config(relay_config *interface_config, int *local_sock, int filter) { - struct ifaddrs *ifa, *ifa_tmp; - sockaddr_in6 non_link_local; - sockaddr_in6 link_local; - - interface_config->local_sock = *local_sock; - interface_config->filter = filter; - - for(auto server: interface_config->servers) { - sockaddr_in6 tmp; - if(inet_pton(AF_INET6, server.c_str(), &tmp.sin6_addr) != 1) - { - syslog(LOG_WARNING, "inet_pton: Failed to convert IPv6 address\n"); - } - tmp.sin6_family = AF_INET6; - tmp.sin6_flowinfo = 0; - tmp.sin6_port = htons(RELAY_PORT); - tmp.sin6_scope_id = 0; - interface_config->servers_sock.push_back(tmp); - } - - if (getifaddrs(&ifa) == -1) { - syslog(LOG_WARNING, "getifaddrs: Unable to get network interfaces\n"); - exit(1); - } - - ifa_tmp = ifa; - while (ifa_tmp) { - if (ifa_tmp->ifa_addr->sa_family == AF_INET6) { - struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr; - if((strcmp(ifa_tmp->ifa_name, interface_config->interface.c_str()) == 0) && !IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { - non_link_local = *in6; - break; - } - if((strcmp(ifa_tmp->ifa_name, interface_config->interface.c_str()) == 0) && IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { - link_local = *in6; - } - } - ifa_tmp = ifa_tmp->ifa_next; - } - freeifaddrs(ifa); - - if(!IN6_IS_ADDR_LINKLOCAL(&non_link_local.sin6_addr)) { - interface_config->link_address = non_link_local; - } - else { - interface_config->link_address = link_local; - } -} - -/** - * @code prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index); - * - * @brief prepare L3 socket for sending - * - * @param local_sock pointer to socket binded to global address for relaying client message to server and listening for server message - * @param server_sock pointer to socket binded to link_local address for relaying server message to client - * @param index scope id of interface - * - * @return none - */ -void prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index) { - struct ifaddrs *ifa, *ifa_tmp; - sockaddr_in6 addr; - sockaddr_in6 ll_addr; - memset(&addr, 0, sizeof(addr)); - memset(&ll_addr, 0, sizeof(ll_addr)); - - if ((*local_sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { - syslog(LOG_ERR, "socket: Failed to create socket\n"); - } - - if ((*server_sock= socket(AF_INET6, SOCK_DGRAM, 0)) == -1) { - syslog(LOG_ERR, "socket: Failed to create socket\n"); - } - - - if (getifaddrs(&ifa) == -1) { - syslog(LOG_WARNING, "getifaddrs: Unable to get network interfaces\n"); - exit(1); - } - - ifa_tmp = ifa; - while (ifa_tmp) { - if (ifa_tmp->ifa_addr->sa_family == AF_INET6) { - struct sockaddr_in6 *in6 = (struct sockaddr_in6*) ifa_tmp->ifa_addr; - if((strcmp(ifa_tmp->ifa_name, config->interface.c_str()) == 0) && !IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { - in6->sin6_family = AF_INET6; - in6->sin6_port = htons(RELAY_PORT); - addr = *in6; - } - if((strcmp(ifa_tmp->ifa_name, config->interface.c_str()) == 0) && IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { - in6->sin6_family = AF_INET6; - in6->sin6_port = htons(RELAY_PORT); - ll_addr = *in6; - } - } - ifa_tmp = ifa_tmp->ifa_next; - } - freeifaddrs(ifa); - - if (bind(*local_sock, (sockaddr *)&addr, sizeof(addr)) == -1) { - syslog(LOG_ERR, "bind: Failed to bind to socket\n"); - } - - if (bind(*server_sock, (sockaddr *)&ll_addr, sizeof(addr)) == -1) { - syslog(LOG_ERR, "bind: Failed to bind to socket\n"); - } -} - - -/** - * @code relay_client(int sock, const uint8_t *msg, int32_t len, ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config); - * - * @brief construct relay-forward message - * - * @param sock L3 socket for sending data to servers - * @param msg pointer to dhcpv6 message header position - * @param len size of data received - * @param ip_hdr pointer to IPv6 header - * @param ether_hdr pointer to Ethernet header - * @param config pointer to the relay interface config - * - * @return none - */ -void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config) { - static uint8_t buffer[4096]; - auto current_buffer_position = buffer; - dhcpv6_relay_msg new_message; - new_message.msg_type = DHCPv6_MESSAGE_TYPE_RELAY_FORW; - memcpy(&new_message.peer_address, &ip_hdr->ip6_src, sizeof(in6_addr)); - new_message.hop_count = 0; - - memcpy(&new_message.link_address, &config->link_address.sin6_addr, sizeof(in6_addr)); - memcpy(current_buffer_position, &new_message, sizeof(dhcpv6_relay_msg)); - current_buffer_position += sizeof(dhcpv6_relay_msg); - - if(config->is_option_79) { - linklayer_addr_option option79; - option79.link_layer_type = htons(1); - option79.option_code = htons(OPTION_CLIENT_LINKLAYER_ADDR); - option79.option_length = htons(2 + 6); // link_layer_type field + address - - memcpy(current_buffer_position, &option79, sizeof(linklayer_addr_option)); - current_buffer_position += sizeof(linklayer_addr_option); - - memcpy(current_buffer_position, ðer_hdr->ether_shost, sizeof(ether_hdr->ether_shost)); - current_buffer_position += sizeof(ether_hdr->ether_shost); - } - - auto dhcp_message_length = len; - relay_forward(current_buffer_position, parse_dhcpv6_hdr(msg), dhcp_message_length); - current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option); - - for(auto server: config->servers_sock) { - send_udp(sock, buffer, server, current_buffer_position - buffer, config, new_message.msg_type); - } -} - -/** - * @code relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config) - * - * @brief construct a relay-forward message encapsulated relay-forward message - * - * @param sock L3 socket for sending data to servers - * @param msg pointer to dhcpv6 message header position - * @param len size of data received - * @param ip_hdr pointer to IPv6 header - * @param config pointer to the relay interface config - * - * @return none - */ -void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config) { - static uint8_t buffer[4096]; - dhcpv6_relay_msg new_message; - auto current_buffer_position = buffer; - auto dhcp_relay_header = parse_dhcpv6_relay(msg); - - if (dhcp_relay_header->hop_count >= HOP_LIMIT) - return; - - new_message.msg_type = DHCPv6_MESSAGE_TYPE_RELAY_FORW; - memcpy(&new_message.peer_address, &ip_hdr->ip6_src, sizeof(in6_addr)); - new_message.hop_count = dhcp_relay_header->hop_count + 1; - - memset(&new_message.link_address, 0, sizeof(in6_addr)); - - memcpy(current_buffer_position, &new_message, sizeof(dhcpv6_relay_msg)); - current_buffer_position += sizeof(dhcpv6_relay_msg); - - auto dhcp_message_length = len; - relay_forward(current_buffer_position, parse_dhcpv6_hdr(msg), dhcp_message_length); - current_buffer_position += dhcp_message_length + sizeof(dhcpv6_option); - - for(auto server: config->servers_sock) { - send_udp(sock, buffer, server, current_buffer_position - buffer, config, new_message.msg_type); - } -} - -/** - * @code relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs); - * - * @brief relay and unwrap a relay-reply message - * - * @param sock L3 socket for sending data to servers - * @param msg pointer to dhcpv6 message header position - * @param len size of data received - * @param config relay interface config - * - * @return none - */ - void relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *config) { - static uint8_t buffer[4096]; - uint8_t type = 0; - struct sockaddr_in6 target_addr; - auto current_buffer_position = buffer; - auto current_position = msg; - const uint8_t *tmp = NULL; - auto dhcp_relay_header = parse_dhcpv6_relay(msg); - current_position += sizeof(struct dhcpv6_relay_msg); - - auto position = current_position + sizeof(struct dhcpv6_option); - auto dhcpv6msg = parse_dhcpv6_hdr(position); - - while ((current_position - msg) < len) { - auto option = parse_dhcpv6_opt(current_position, &tmp); - current_position = tmp; - if (current_position - msg > len || ntohs(option->option_length) > sizeof(buffer) - (current_buffer_position - buffer)) { - break; - } - switch (ntohs(option->option_code)) { - case OPTION_RELAY_MSG: - memcpy(current_buffer_position, ((uint8_t *)option) + sizeof(struct dhcpv6_option), ntohs(option->option_length)); - current_buffer_position += ntohs(option->option_length); - type = dhcpv6msg->msg_type; - break; - default: - break; - } - } - - memcpy(&target_addr.sin6_addr, &dhcp_relay_header->peer_address, sizeof(struct in6_addr)); - target_addr.sin6_family = AF_INET6; - target_addr.sin6_flowinfo = 0; - target_addr.sin6_port = htons(CLIENT_PORT); - target_addr.sin6_scope_id = if_nametoindex(config->interface.c_str()); - - send_udp(sock, buffer, target_addr, current_buffer_position - buffer, config, type); -} - - -/** - * @code callback(evutil_socket_t fd, short event, void *arg); - * - * @brief callback for libevent that is called everytime data is received at the filter socket - * - * @param fd filter socket - * @param event libevent triggered event - * @param arg callback argument provided by user - * - * @return none - */ -void callback(evutil_socket_t fd, short event, void *arg) { - struct relay_config *config = (struct relay_config *)(arg); - static uint8_t message_buffer[4096]; - int32_t len = recv(config->filter, message_buffer, 4096, 0); - if (len <= 0) { - syslog(LOG_WARNING, "recv: Failed to receive data at filter socket: %s\n", strerror(errno)); - return; - } - - char* ptr = (char *)message_buffer; - const uint8_t *current_position = (uint8_t *)ptr; - const uint8_t *tmp = NULL; - const uint8_t *prev = NULL; - - auto ether_header = parse_ether_frame(current_position, &tmp); - current_position = tmp; - - auto ip_header = parse_ip6_hdr(current_position, &tmp); - current_position = tmp; - - prev = current_position; - if (ip_header->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_UDP) { - const struct ip6_ext *ext_header; - do { - ext_header = (const struct ip6_ext *)current_position; - current_position += ext_header->ip6e_len; - if((current_position == prev) || (current_position >= (uint8_t *)ptr + sizeof(message_buffer))) { - return; - } - prev = current_position; - } - while (ext_header->ip6e_nxt != IPPROTO_UDP); - } - - auto udp_header = parse_udp(current_position, &tmp); - current_position = tmp; - - auto msg = parse_dhcpv6_hdr(current_position); - counters[msg->msg_type]++; - std::string counterVlan = counter_table; - update_counter(counterVlan.append(config->interface), msg->msg_type); - - switch (msg->msg_type) { - case DHCPv6_MESSAGE_TYPE_RELAY_FORW: - { - relay_relay_forw(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, config); - break; - } - default: - { - relay_client(config->local_sock, current_position, ntohs(udp_header->len) - sizeof(udphdr), ip_header, ether_header, config); - break; - } - } -} - -/** - * @code void server_callback(evutil_socket_t fd, short event, void *arg); - * - * @brief callback for libevent that is called everytime data is received at the server socket - * - * @param fd filter socket - * @param event libevent triggered event - * @param arg callback argument provided by user - * - * @return none - */ -void server_callback(evutil_socket_t fd, short event, void *arg) { - struct relay_config *config = (struct relay_config *)arg; - sockaddr_in6 from; - socklen_t len = sizeof(from); - int32_t data = 0; - static uint8_t message_buffer[4096]; - - if ((data = recvfrom(config->local_sock, message_buffer, 4096, 0, (sockaddr *)&from, &len)) == -1) { - syslog(LOG_WARNING, "recv: Failed to receive data from server\n"); - } - - auto msg = parse_dhcpv6_hdr(message_buffer); - - counters[msg->msg_type]++; - std::string counterVlan = counter_table; - update_counter(counterVlan.append(config->interface), msg->msg_type); - if (msg->msg_type == DHCPv6_MESSAGE_TYPE_RELAY_REPL) { - relay_relay_reply(config->server_sock, message_buffer, data, config); - } -} - -/** - * @code signal_init(); - * - * @brief initialize DHCPv6 Relay libevent signals - */ -int signal_init() { - int rv = -1; - do { - ev_sigint = evsignal_new(base, SIGINT, signal_callback, base); - if (ev_sigint == NULL) { - syslog(LOG_ERR, "Could not create SIGINT libevent signal\n"); - break; - } - - ev_sigterm = evsignal_new(base, SIGTERM, signal_callback, base); - if (ev_sigterm == NULL) { - syslog(LOG_ERR, "Could not create SIGTERM libevent signal\n"); - break; - } - rv = 0; - } while(0); - return rv; -} - -/** - * @code signal_start(); - * - * @brief start DHCPv6 Relay libevent base and add signals - */ -int signal_start() -{ - int rv = -1; - do - { - if (evsignal_add(ev_sigint, NULL) != 0) { - syslog(LOG_ERR, "Could not add SIGINT libevent signal\n"); - break; - } - - if (evsignal_add(ev_sigterm, NULL) != 0) { - syslog(LOG_ERR, "Could not add SIGTERM libevent signal\n"); - break; - } - - if (event_base_dispatch(base) != 0) { - syslog(LOG_ERR, "Could not start libevent dispatching loop\n"); - } - - rv = 0; - } while (0); - - return rv; -} - -/** - * @code signal_callback(fd, event, arg); - * - * @brief signal handler for dhcp6relay. Initiate shutdown when signal is caught - * - * @param fd libevent socket - * @param event event triggered - * @param arg pointer to libevent base - * - * @return none - */ -void signal_callback(evutil_socket_t fd, short event, void *arg) -{ - syslog(LOG_ALERT, "Received signal: '%s'\n", strsignal(fd)); - if ((fd == SIGTERM) || (fd == SIGINT)) { - dhcp6relay_stop(); - } -} - -/** - * @code dhcp6relay_stop(); - * - * @brief stop DHCPv6 Relay libevent loop upon signal - */ -void dhcp6relay_stop() -{ - event_base_loopexit(base, NULL); -} - -/** - * @code loop_relay(std::vector *vlans); - * - * @brief main loop: configure sockets, create libevent base, start server listener thread - * - * @param vlans list of vlans retrieved from config_db - */ -void loop_relay(std::vector *vlans) { - std::vector sockets; - base = event_base_new(); - if(base == NULL) { - syslog(LOG_ERR, "libevent: Failed to create base\n"); - } - - for(relay_config &vlan : *vlans) { - relay_config *config = &vlan; - int filter = 0; - int local_sock = 0; - int server_sock = 0; - int index = if_nametoindex(config->interface.c_str()); - - std::string counterVlan = counter_table; - initialize_counter(counterVlan.append(config->interface)); - - filter = sock_open(index, ðer_relay_fprog); - prepare_socket(&local_sock, &server_sock, config, index); - - config->local_sock = local_sock; - config->server_sock = server_sock; - - sockets.push_back(filter); - sockets.push_back(local_sock); - sockets.push_back(server_sock); - - prepare_relay_config(config, &local_sock, filter); - - evutil_make_listen_socket_reuseable(filter); - evutil_make_socket_nonblocking(filter); - - evutil_make_listen_socket_reuseable(local_sock); - evutil_make_socket_nonblocking(local_sock); - - listen_event = event_new(base, filter, EV_READ|EV_PERSIST, callback, config); - server_listen_event = event_new(base, local_sock, EV_READ|EV_PERSIST, server_callback, config); - if (listen_event == NULL || server_listen_event == NULL) { - syslog(LOG_ERR, "libevent: Failed to create libevent\n"); - } - - event_add(listen_event, NULL); - event_add(server_listen_event, NULL); - } - - if((signal_init() == 0) && signal_start() == 0) { - shutdown(); - for(std::size_t i = 0; i -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#define PACKED __attribute__ ((packed)) - -#define RELAY_PORT 547 -#define CLIENT_PORT 546 -#define HOP_LIMIT 8 //HOP_LIMIT reduced from 32 to 8 as stated in RFC8415 - -#define lengthof(A) (sizeof (A) / sizeof (A)[0]) - -#define OPTION_RELAY_MSG 9 -#define OPTION_CLIENT_LINKLAYER_ADDR 79 - -/* DHCPv6 message types */ -typedef enum -{ - DHCPv6_MESSAGE_TYPE_UNKNOWN = 0, - DHCPv6_MESSAGE_TYPE_SOLICIT = 1, - DHCPv6_MESSAGE_TYPE_ADVERTISE = 2, - DHCPv6_MESSAGE_TYPE_REQUEST = 3, - DHCPv6_MESSAGE_TYPE_CONFIRM = 4, - DHCPv6_MESSAGE_TYPE_RENEW = 5, - DHCPv6_MESSAGE_TYPE_REBIND = 6, - DHCPv6_MESSAGE_TYPE_REPLY = 7, - DHCPv6_MESSAGE_TYPE_RELEASE = 8, - DHCPv6_MESSAGE_TYPE_DECLINE = 9, - DHCPv6_MESSAGE_TYPE_RELAY_FORW = 12, - DHCPv6_MESSAGE_TYPE_RELAY_REPL = 13, - - DHCPv6_MESSAGE_TYPE_COUNT -} dhcp_message_type_t; - -struct relay_config { - int local_sock; - int server_sock; - int filter; - sockaddr_in6 link_address; - std::string interface; - std::vector servers; - std::vector servers_sock; - bool is_option_79; -}; - - -/* DHCPv6 messages and options */ - -struct dhcpv6_msg { - uint8_t msg_type; -}; - -struct PACKED dhcpv6_relay_msg { - uint8_t msg_type; - uint8_t hop_count; - struct in6_addr link_address; - struct in6_addr peer_address; -}; - - -struct dhcpv6_option { - uint16_t option_code; - uint16_t option_length; -}; - -struct linklayer_addr_option { - uint16_t option_code; - uint16_t option_length; - uint16_t link_layer_type; -}; - -/** - * @code sock_open(int ifindex, const struct sock_fprog *fprog); - * - * @brief prepare L2 socket to attach to "udp and port 547" filter - * - * @param ifindex interface index - * @param fprog bpf filter "udp and port 547" - * - * @return socket descriptor - */ -int sock_open(int ifindex, const struct sock_fprog *fprog); - -/** - * @code prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index); - * - * @brief prepare L3 socket for sending - * - * @param local_sock pointer to socket binded to global address for relaying client message to server and listening for server message - * @param server_sock pointer to socket binded to link_local address for relaying server message to client - * @param index scope id of interface - * - * @return none - */ -void prepare_socket(int *local_sock, int *server_sock, relay_config *config, int index); - -/** - * @code prepare_relay_config(relay_config *interface_config, int *local_sock, int filter); - * - * @brief prepare for specified relay interface config: server and link address - * - * @param interface_config pointer to relay config to be prepared - * @param local_sock L3 socket used for relaying messages - * @param filter socket attached with filter - * - * @return none - */ -void prepare_relay_config(relay_config *interface_config, int *local_sock, int filter); - -/** - * @code relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length); - * - * @brief embed the DHCPv6 message received into DHCPv6 relay forward message - * - * @param buffer pointer to buffer - * @param msg pointer to parsed DHCPv6 message - * @param msg_length length of DHCPv6 message - * - * @return none - */ -void relay_forward(uint8_t *buffer, const struct dhcpv6_msg *msg, uint16_t msg_length); - -/** - * @code relay_client(int sock, const uint8_t *msg, int32_t len, ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config); - * - * @brief construct relay-forward message - * - * @param sock L3 socket for sending data to servers - * @param msg pointer to dhcpv6 message header position - * @param len size of data received - * @param ip_hdr pointer to IPv6 header - * @param ether_hdr pointer to Ethernet header - * @param config pointer to the relay interface config - * - * @return none - */ -void relay_client(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, const ether_header *ether_hdr, relay_config *config); - -/** - * @code relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config) - * - * @brief construct a relay-forward message encapsulated relay-forward message - * - * @param sock L3 socket for sending data to servers - * @param msg pointer to dhcpv6 message header position - * @param len size of data received - * @param ip_hdr pointer to IPv6 header - * @param config pointer to the relay interface config - * - * @return none - */ -void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr *ip_hdr, relay_config *config); - -/** - * @code relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs); - * - * @brief relay and unwrap a relay-reply message - * - * @param sock L3 socket for sending data to servers - * @param msg pointer to dhcpv6 message header position - * @param len size of data received - * @param config relay interface config - * - * @return none - */ -void relay_relay_reply(int sock, const uint8_t *msg, int32_t len, relay_config *configs); - -/** - * @code loop_relay(std::vector *vlans); - * - * @brief main loop: configure sockets, create libevent base, start server listener thread - * - * @param vlans list of vlans retrieved from config_db - */ -void loop_relay(std::vector *vlans); - -/** - * @code signal_init(); - * - * @brief initialize DHCPv6 Relay libevent signals - */ -int signal_init(); - -/** - * @code signal_start(); - * - * @brief start DHCPv6 Relay libevent base and add signals - */ -int signal_start(); - -/** - * @code dhcp6relay_stop(); - * - * @brief stop DHCPv6 Relay libevent loop upon signal - */ -void dhcp6relay_stop(); - -/** - * @code signal_callback(fd, event, arg); - * - * @brief signal handler for dhcp6relay. Initiate shutdown when signal is caught - * - * @param fd libevent socket - * @param event event triggered - * @param arg pointer to libevent base - * - * @return none - */ -void signal_callback(evutil_socket_t fd, short event, void *arg); - -/** - * @code shutdown(); - * - * @brief free signals and terminate threads - */ -void shutdown(); - -/** - * @code void initialize_counter(std::string counterVlan) - * - * @brief initialize the counter by each Vlan - * - * @param counterVlan counter table with interface name - * - * @return none - */ -void initialize_counter(std::string counterVlan); - -/** - * @code void update_counter(std::string CounterVlan, uint8_t msg_type); - * - * @brief update the counter in state_db with count of each DHCPv6 message type - * - * @param counterVlan counter table with interface name - * @param msg_type dhcpv6 message type to be updated in counter - * - * @return none - */ -void update_counter(std::string counterVlan, uint8_t msg_type); - -/* Helper functions */ - -/** - * @code std::string toString(uint16_t count); - * - * @brief convert uint16_t to string - * - * @param count count of messages in counter - * - * @return count in string - */ -std::string toString(uint16_t count); - -/** - * @code const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end); - * - * @brief parse through ethernet frame - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return ether_header end of ethernet header position - */ -const struct ether_header *parse_ether_frame(const uint8_t *buffer, const uint8_t **out_end); - -/** - * @code const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end); - * - * @brief parse through ipv6 header - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return ip6_hdr end of ipv6 header position - */ -const struct ip6_hdr *parse_ip6_hdr(const uint8_t *buffer, const uint8_t **out_end); - -/** - * @code const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end); - * - * @brief parse through udp header - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return udphdr end of udp header position - */ -const struct udphdr *parse_udp(const uint8_t *buffer, const uint8_t **out_end); - -/** - * @code const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer); - * - * @brief parse through dhcpv6 header - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return dhcpv6_msg end of dhcpv6 header position - */ -const struct dhcpv6_msg *parse_dhcpv6_hdr(const uint8_t *buffer); - -/** - * @code const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer); - * - * @brief parse through dhcpv6 relay message - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return dhcpv6_relay_msg start of dhcpv6 relay message or end of dhcpv6 message type position - */ -const struct dhcpv6_relay_msg *parse_dhcpv6_relay(const uint8_t *buffer); - -/** - * @code const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end); - * - * @brief parse through dhcpv6 option - * - * @param *buffer message buffer - * @param **out_end pointer - * - * @return dhcpv6_option end of dhcpv6 message option - */ -const struct dhcpv6_option *parse_dhcpv6_opt(const uint8_t *buffer, const uint8_t **out_end); - -/** - * @code void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type); - * - * @brief send udp packet - * - * @param *buffer message buffer - * @param sockaddr_in6 target target socket - * @param n length of message - * @param relay_config *config pointer to relay_config - * @param uint8_t msg_type message type of dhcpv6 option of relayed message - * - * @return dhcpv6_option end of dhcpv6 message option - */ -void send_udp(int sock, uint8_t *buffer, struct sockaddr_in6 target, uint32_t n, relay_config *config, uint8_t msg_type); - diff --git a/src/dhcp6relay/src/subdir.mk b/src/dhcp6relay/src/subdir.mk deleted file mode 100644 index c6e784ef5504..000000000000 --- a/src/dhcp6relay/src/subdir.mk +++ /dev/null @@ -1,23 +0,0 @@ -CC := g++ - -C_SRCS += \ -../src/relay.c \ -../src/configInterface.c \ -../src/main.c - -OBJS += \ -./src/relay.o \ -./src/configInterface.o \ -./src/main.o - -C_DEPS += \ -./src/relay.d \ -./src/configInterface.d \ -./src/main.d - -src/%.o: src/%.cpp - @echo 'Building file: $<' - @echo 'Invoking: GCC C++ Compiler' - $(CC) -D__FILENAME__="$(subst src/,,$<)" $(CFLAGS) -c -fmessage-length=0 -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" -\ @echo 'Finished building: $<' - @echo ' ' diff --git a/src/dhcprelay b/src/dhcprelay new file mode 160000 index 000000000000..04a2d4163940 --- /dev/null +++ b/src/dhcprelay @@ -0,0 +1 @@ +Subproject commit 04a2d4163940d3f70b2f2af5c69681edd4a1b0e1 diff --git a/src/sonic-config-engine/minigraph.py b/src/sonic-config-engine/minigraph.py index 9ad536ad7cae..feaf3df76e2b 100644 --- a/src/sonic-config-engine/minigraph.py +++ b/src/sonic-config-engine/minigraph.py @@ -400,31 +400,6 @@ def parse_dpg(dpg, hname): vlans[sonic_vlan_name] = vlan_attributes dhcp_relay_table[sonic_vlan_name] = dhcp_attributes - ''' - dhcp = child.find(str(QName(ns, "Dhcp"))) - dhcp_relay_table = {} - - if dhcp is not None: - for vintf in dhcp.findall(str(QName(ns, "VlanInterface"))): - vintfname = vintf.find(str(QName(ns, "Name"))).text - - dhcp_attributes = {} - - dhcp_node = vintf.find(str(QName(ns, "Dhcpv6Relays"))) - if dhcp_node is not None and dhcp_node.text is not None: - dhcpservers = dhcp_node.text - vdhcpserver_list = dhcpservers.split(';') - dhcp_attributes['dhcpv6_servers'] = vdhcpserver_list - - option_linklayer_addr = vintf.find(str(QName(ns, "Dhcpv6OptionRfc6939"))) - if option_linklayer_addr is not None and option_linklayer_addr.text == "true": - dhcp_attributes['dhcpv6_option|rfc6939_support'] = "true" - elif option_linklayer_addr is not None and option_linklayer_addr.text == "false": - dhcp_attributes['dhcpv6_option|rfc6939_support'] = "false" - - dhcp_relay_table[vintfname] = dhcp_attributes - ''' - acls = {} for aclintf in aclintfs.findall(str(QName(ns, "AclInterface"))): if aclintf.find(str(QName(ns, "InAcl"))) is not None: @@ -1022,7 +997,7 @@ def parse_xml(filename, platform=None, port_config_file=None, asic_name=None): elif child.tag == str(QName(ns, "UngDec")): (u_neighbors, u_devices, _, _, _, _, _, _) = parse_png(child, hostname) elif child.tag == str(QName(ns, "MetadataDeclaration")): - (syslog_servers, dhcp_servers, dhcppv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, downstream_subrole, qos_profile, resource_type) = parse_meta(child, hostname) + (syslog_servers, dhcp_servers, dhcpv6_servers, ntp_servers, tacacs_servers, mgmt_routes, erspan_dst, deployment_id, region, cloudtype, downstream_subrole, qos_profile, resource_type) = parse_meta(child, hostname) elif child.tag == str(QName(ns, "LinkMetadataDeclaration")): linkmetas = parse_linkmeta(child, hostname) elif child.tag == str(QName(ns, "DeviceInfos")): diff --git a/src/sonic-config-engine/tests/test_minigraph_case.py b/src/sonic-config-engine/tests/test_minigraph_case.py index 8c4d638fa711..3ab949fbb0bb 100644 --- a/src/sonic-config-engine/tests/test_minigraph_case.py +++ b/src/sonic-config-engine/tests/test_minigraph_case.py @@ -83,21 +83,31 @@ def test_minigraph_vlans(self): argument = '-m "' + self.sample_graph + '" -p "' + self.port_config + '" -v VLAN' output = self.run_script(argument) expected = { - u'Vlan1000': { - u'alias': 'ab1', - u'dhcp_servers': [u'192.0.0.1', u'192.0.0.2'], - u'dhcpv6_servers': [u'fc02:2000::1', u'fc02:2000::2'], - u'vlanid': u'1000', - u'members': [u'Ethernet8'] - }, - 'Vlan2000': { - u'alias': 'ab2', - u'dhcp_servers': ['192.0.0.3', '192.0.0.4'], - u'members': ['Ethernet4'], - u'dhcpv6_servers': [u'fc02:2000::3', u'fc02:2000::4'], - u'vlanid': '2000' - } - } + 'Vlan1000': { + 'alias': 'ab1', + 'dhcp_servers': [ + '192.0.0.1', + '192.0.0.2' + ], + 'dhcpv6_servers': [ + 'fc02:2000::1', + 'fc02:2000::2' + ], + 'vlanid': '1000', + 'members': ['Ethernet8'] + }, + 'Vlan2000': { + 'alias': 'ab2', + 'dhcp_servers': [ + '192.0.0.3', + '192.0.0.4'], + 'members': ['Ethernet4'], + 'dhcpv6_servers': [ + 'fc02:2000::3', + 'fc02:2000::4'], + 'vlanid': '2000' + } + } self.assertEqual( utils.to_dict(output.strip()), diff --git a/src/sonic-dhcp-relay b/src/sonic-dhcp-relay deleted file mode 160000 index e2cd5851ddd9..000000000000 --- a/src/sonic-dhcp-relay +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e2cd5851ddd9b6496b59b72e75aaba8e4d7f284d From 81ff36a057c9f75ef760a75b5be179b5cd19d792 Mon Sep 17 00:00:00 2001 From: kellyyeh <42761586+kellyyeh@users.noreply.github.com> Date: Tue, 11 Oct 2022 06:15:36 -0700 Subject: [PATCH 5/7] Add package dependency --- rules/docker-dhcp-relay.mk | 2 +- sonic-slave-stretch/Dockerfile.j2 | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/rules/docker-dhcp-relay.mk b/rules/docker-dhcp-relay.mk index 4182d3ec137f..8d44ee2a432b 100644 --- a/rules/docker-dhcp-relay.mk +++ b/rules/docker-dhcp-relay.mk @@ -6,7 +6,7 @@ DOCKER_DHCP_RELAY_DBG = $(DOCKER_DHCP_RELAY_STEM)-$(DBG_IMAGE_MARK).gz $(DOCKER_DHCP_RELAY)_PATH = $(DOCKERS_PATH)/$(DOCKER_DHCP_RELAY_STEM) -$(DOCKER_DHCP_RELAY)_DEPENDS += $(ISC_DHCP_RELAY) $(REDIS_TOOLS) $(SONIC_DHCPMON) $(SONIC_DHCPRELAY) $(LIBSWSSCOMMON) +$(DOCKER_DHCP_RELAY)_DEPENDS += $(ISC_DHCP_RELAY) $(REDIS_TOOLS) $(LIBSWSSCOMMON) $(SONIC_DHCPMON) $(SONIC_DHCPRELAY) $(DOCKER_DHCP_RELAY)_DBG_DEPENDS = $($(DOCKER_CONFIG_ENGINE_STRETCH)_DBG_DEPENDS) $(DOCKER_DHCP_RELAY)_DBG_DEPENDS += $(ISC_DHCP_RELAY_DBG) $(SONIC_DHCPRELAY_DBG) diff --git a/sonic-slave-stretch/Dockerfile.j2 b/sonic-slave-stretch/Dockerfile.j2 index a39f21be63aa..991eae773d6f 100644 --- a/sonic-slave-stretch/Dockerfile.j2 +++ b/sonic-slave-stretch/Dockerfile.j2 @@ -301,7 +301,14 @@ RUN apt-get update && apt-get install -y \ xxd \ # For DHCP Monitor tool libexplain-dev \ - libevent-dev + libevent-dev \ +# For dhcp6relay + libboost-dev \ + libboost-system-dev \ + libboost-thread-dev \ + libboost-atomic-dev \ + libboost-chrono-dev \ + libboost-date-time-dev ## Config dpkg ## install the configuration file if it’s currently missing From 3f996df1c7827a465bf45f3c7f48fc78eac24c9c Mon Sep 17 00:00:00 2001 From: kellyyeh Date: Sat, 22 Oct 2022 00:30:52 +0000 Subject: [PATCH 6/7] Clean Up --- dockers/docker-dhcp-relay/dhcpv6-relay.agents.j2 | 3 ++- .../tests/sample_output/docker-dhcp-relay.supervisord.conf | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dockers/docker-dhcp-relay/dhcpv6-relay.agents.j2 b/dockers/docker-dhcp-relay/dhcpv6-relay.agents.j2 index 3b14d11e3bcd..134280e52313 100644 --- a/dockers/docker-dhcp-relay/dhcpv6-relay.agents.j2 +++ b/dockers/docker-dhcp-relay/dhcpv6-relay.agents.j2 @@ -11,12 +11,13 @@ {% set _dummy = relay_for_ipv6.update({'flag': False}) %} [program:dhcp6relay] command=/usr/sbin/dhcp6relay - priority=3 autostart=false autorestart=false stdout_logfile=syslog stderr_logfile=syslog +dependent_startup=true +dependent_startup_wait_for=start:exited {% endif %} {% endif %} diff --git a/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf b/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf index 27c409f45c14..4756773d05ec 100644 --- a/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf +++ b/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf @@ -56,7 +56,6 @@ dependent_startup_wait_for=start:exited [program:dhcp6relay] command=/usr/sbin/dhcp6relay - priority=3 autostart=false autorestart=false From 5bab10c0d6dc58b981544c5275635b4f858166e9 Mon Sep 17 00:00:00 2001 From: kellyyeh <42761586+kellyyeh@users.noreply.github.com> Date: Fri, 21 Oct 2022 22:22:48 -0700 Subject: [PATCH 7/7] Update docker-dhcp-relay.supervisord.conf --- .../tests/sample_output/docker-dhcp-relay.supervisord.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf b/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf index 4756773d05ec..3ade01edee2c 100644 --- a/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf +++ b/src/sonic-config-engine/tests/sample_output/docker-dhcp-relay.supervisord.conf @@ -61,6 +61,8 @@ autostart=false autorestart=false stdout_logfile=syslog stderr_logfile=syslog +dependent_startup=true +dependent_startup_wait_for=start:exited [group:dhcpmon] programs=dhcpmon-Vlan1000