From ed4a493b734cac1fbf73df4a067a12d52d3fea57 Mon Sep 17 00:00:00 2001 From: Gabriel Skupien Date: Sun, 26 Apr 2020 12:23:58 +0200 Subject: dhclient: T1381: Improved RFC3442 routes processing Several improvements in processing RFC3442 routes (support for route deletion, DHCP RENEW and link-local routes) --- .../dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper | 8 +- src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup | 64 ++++++++- .../02-vyos-dhcp-renew-rfc3442 | 148 +++++++++++++++++++++ 3 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 src/etc/dhcp/dhclient-exit-hooks.d/02-vyos-dhcp-renew-rfc3442 (limited to 'src/etc') diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper index 59f92703c..f1167fcd2 100644 --- a/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper +++ b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper @@ -15,8 +15,11 @@ function frr_alive () { # convert ip route command to vtysh function iptovtysh () { # prepare variables for vtysh command - VTYSH_DISTANCE="210" - VTYSH_TAG="210" + local VTYSH_DISTANCE="210" + local VTYSH_TAG="210" + local VTYSH_NETADDR="" + local VTYSH_GATEWAY="" + local VTYSH_DEV="" # convert default route to 0.0.0.0/0 if [ "$4" == "default" ] ; then VTYSH_NETADDR="0.0.0.0/0" @@ -74,3 +77,4 @@ function ip () { fi fi } + diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup index ce846f6c3..88a4d9db9 100644 --- a/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup +++ b/src/etc/dhcp/dhclient-exit-hooks.d/01-vyos-cleanup @@ -1,12 +1,74 @@ +# NOTE: here we use 'ip' wrapper, therefore a route will be actually deleted via /usr/sbin/ip or vtysh, according to the system state + if [[ $reason =~ (EXPIRE|FAIL|RELEASE|STOP) ]]; then # delete dynamic nameservers from a configuration if lease was deleted logmsg info "Deleting nameservers with tag \"dhcp-${interface}\" via vyos-hostsd-client" vyos-hostsd-client --delete-name-servers --tag dhcp-${interface} - # try to delete default ip route (NOTE: here we use 'ip' wrapper, therefore a route will be actually deleted via /usr/sbin/ip or vtysh, according to the system state) + # try to delete default ip route for router in $old_routers; do logmsg info "Deleting default route: via $router dev ${interface}" ip -4 route del default via $router dev ${interface} done + # delete rfc3442 routes + if [ -n "$old_rfc3442_classless_static_routes" ]; then + set -- $old_rfc3442_classless_static_routes + while [ $# -gt 0 ]; do + net_length=$1 + via_arg='' + case $net_length in + 32|31|30|29|28|27|26|25) + if [ $# -lt 9 ]; then + return 1 + fi + net_address="${2}.${3}.${4}.${5}" + gateway="${6}.${7}.${8}.${9}" + shift 9 + ;; + 24|23|22|21|20|19|18|17) + if [ $# -lt 8 ]; then + return 1 + fi + net_address="${2}.${3}.${4}.0" + gateway="${5}.${6}.${7}.${8}" + shift 8 + ;; + 16|15|14|13|12|11|10|9) + if [ $# -lt 7 ]; then + return 1 + fi + net_address="${2}.${3}.0.0" + gateway="${4}.${5}.${6}.${7}" + shift 7 + ;; + 8|7|6|5|4|3|2|1) + if [ $# -lt 6 ]; then + return 1 + fi + net_address="${2}.0.0.0" + gateway="${3}.${4}.${5}.${6}" + shift 6 + ;; + 0) # default route + if [ $# -lt 5 ]; then + return 1 + fi + net_address="0.0.0.0" + gateway="${2}.${3}.${4}.${5}" + shift 5 + ;; + *) # error + return 1 + ;; + esac + # take care of link-local routes + if [ "${gateway}" != '0.0.0.0' ]; then + via_arg="via ${gateway}" + fi + # delete route (ip detects host routes automatically) + ip -4 route del "${net_address}/${net_length}" \ + ${via_arg} dev "${interface}" >/dev/null 2>&1 + done + fi fi if [[ $reason =~ (EXPIRE6|RELEASE6|STOP6) ]]; then diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/02-vyos-dhcp-renew-rfc3442 b/src/etc/dhcp/dhclient-exit-hooks.d/02-vyos-dhcp-renew-rfc3442 new file mode 100644 index 000000000..9202fe72d --- /dev/null +++ b/src/etc/dhcp/dhclient-exit-hooks.d/02-vyos-dhcp-renew-rfc3442 @@ -0,0 +1,148 @@ +# support for RFC3442 routes in DHCP RENEW + +function convert_to_cidr () { + cidr="" + set -- $1 + while [ $# -gt 0 ]; do + net_length=$1 + + case $net_length in + 32|31|30|29|28|27|26|25) + if [ $# -lt 9 ]; then + return 1 + fi + net_address="${2}.${3}.${4}.${5}" + gateway="${6}.${7}.${8}.${9}" + shift 9 + ;; + 24|23|22|21|20|19|18|17) + if [ $# -lt 8 ]; then + return 1 + fi + net_address="${2}.${3}.${4}.0" + gateway="${5}.${6}.${7}.${8}" + shift 8 + ;; + 16|15|14|13|12|11|10|9) + if [ $# -lt 7 ]; then + return 1 + fi + net_address="${2}.${3}.0.0" + gateway="${4}.${5}.${6}.${7}" + shift 7 + ;; + 8|7|6|5|4|3|2|1) + if [ $# -lt 6 ]; then + return 1 + fi + net_address="${2}.0.0.0" + gateway="${3}.${4}.${5}.${6}" + shift 6 + ;; + 0) # default route + if [ $# -lt 5 ]; then + return 1 + fi + net_address="0.0.0.0" + gateway="${2}.${3}.${4}.${5}" + shift 5 + ;; + *) # error + return 1 + ;; + esac + + cidr+="${net_address}/${net_length}:${gateway} " + done +} + +# main script starts here + +RUN="yes" + +if [ "$RUN" = "yes" ]; then + convert_to_cidr "$old_rfc3442_classless_static_routes" + old_cidr=$cidr + convert_to_cidr "$new_rfc3442_classless_static_routes" + new_cidr=$cidr + + if [ "$reason" = "RENEW" ]; then + if [ "$new_rfc3442_classless_static_routes" != "$old_rfc3442_classless_static_routes" ]; then + logmsg info "RFC3442 route change detected, old_routes: $old_rfc3442_classless_static_routes" + logmsg info "RFC3442 route change detected, new_routes: $new_rfc3442_classless_static_routes" + if [ -z "$new_rfc3442_classless_static_routes" ]; then + # delete all routes from the old_rfc3442_classless_static_routes + for route in $old_cidr; do + network=$(printf "${route}" | awk -F ":" '{print $1}') + gateway=$(printf "${route}" | awk -F ":" '{print $2}') + # take care of link-local routes + if [ "${gateway}" != '0.0.0.0' ]; then + via_arg="via ${gateway}" + else + via_arg="" + fi + ip -4 route del "${network}" "${via_arg}" dev "${interface}" >/dev/null 2>&1 + done + elif [ -z "$old_rfc3442_classless_static_routes" ]; then + # add all routes from the new_rfc3442_classless_static_routes + for route in $new_cidr; do + network=$(printf "${route}" | awk -F ":" '{print $1}') + gateway=$(printf "${route}" | awk -F ":" '{print $2}') + # take care of link-local routes + if [ "${gateway}" != '0.0.0.0' ]; then + via_arg="via ${gateway}" + else + via_arg="" + fi + ip -4 route add "${network}" "${via_arg}" dev "${interface}" >/dev/null 2>&1 + done + else + # update routes + # delete old + for old_route in $old_cidr; do + match="false" + for new_route in $new_cidr; do + if [[ "$old_route" == "$new_route" ]]; then + match="true" + break + fi + done + if [[ "$match" == "false" ]]; then + # delete old_route + network=$(printf "${old_route}" | awk -F ":" '{print $1}') + gateway=$(printf "${old_route}" | awk -F ":" '{print $2}') + # take care of link-local routes + if [ "${gateway}" != '0.0.0.0' ]; then + via_arg="via ${gateway}" + else + via_arg="" + fi + ip -4 route del "${network}" "${via_arg}" dev "${interface}" >/dev/null 2>&1 + fi + done + # add new + for new_route in $new_cidr; do + match="false" + for old_route in $old_cidr; do + if [[ "$new_route" == "$old_route" ]]; then + match="true" + break + fi + done + if [[ "$match" == "false" ]]; then + # add new_route + network=$(printf "${new_route}" | awk -F ":" '{print $1}') + gateway=$(printf "${new_route}" | awk -F ":" '{print $2}') + # take care of link-local routes + if [ "${gateway}" != '0.0.0.0' ]; then + via_arg="via ${gateway}" + else + via_arg="" + fi + ip -4 route add "${network}" "${via_arg}" dev "${interface}" >/dev/null 2>&1 + fi + done + fi + fi + fi +fi -- cgit v1.2.3