5 # convert number of mask bits to x.x.x.x or x:x:x:x:x:x:x:x mask format
8 local full_octets=$(($1/8))
9 local partial_octet=$(($1%8))
11 for ((i=0;i<4;i+=1)); do
12 if [ $i -lt $full_octets ]; then
14 elif [ $i -eq $full_octets ]; then
15 mask+=$((256 - 2**(8-$partial_octet)))
19 test $i -lt 3 && mask+=.
28 if [ $num_bits -le 128 ]; then
29 local full_blocks=$((num_bits / 16))
30 local remaining_bits=$((16 - (num_bits % 16)))
32 for ((i = 0; i < 8; i++)); do
33 if [ $i -lt $full_blocks ]; then
35 elif [ $i -eq $full_blocks ]; then
36 mask+="$(printf "%x" $((0xFFFF >> $remaining_bits)))"
40 [ $i -lt 7 ] && mask+=":"
43 echo "Invalid prefix length for IPv6"
49 # apply netmask (second argument) to ip address (first argument)
51 local ipa=$(echo ${1} | awk -F. '{ print $1 }')
52 local ipb=$(echo ${1} | awk -F. '{ print $2 }')
53 local ipc=$(echo ${1} | awk -F. '{ print $3 }')
54 local ipd=$(echo ${1} | awk -F. '{ print $4 }')
55 local mka=$(echo ${2} | awk -F. '{ print $1 }')
56 local mkb=$(echo ${2} | awk -F. '{ print $2 }')
57 local mkc=$(echo ${2} | awk -F. '{ print $3 }')
58 local mkd=$(echo ${2} | awk -F. '{ print $4 }')
59 local nta="$(( $ipa & $mka ))"
60 local ntb="$(( $ipb & $mkb ))"
61 local ntc="$(( $ipc & $mkc ))"
62 local ntd="$(( $ipd & $mkd ))"
63 echo "$nta.$ntb.$ntc.$ntd"
66 # expand IPv6 address to full length
71 # Split the IP address into segments
72 IFS=':' read -r -a segments <<< "$ip"
74 # Count the number of segments
75 local num_segments=${#segments[@]}
77 # Check if "::" is present in the IP address
78 local double_colon_index=$(echo "$ip" | grep -o "::" | wc -l)
79 local double_colon_present=false
81 # If "::" is present, count the number of segments before and after it
82 if [[ "$double_colon_index" -gt 0 ]]; then
83 double_colon_present=true
84 local before_double_colon=${segments[0]}
85 local after_double_colon=${segments[-1]}
87 # Count the number of segments before "::"
88 local num_before_double_colon=$(echo "$before_double_colon" | grep -o ":" | wc -l)
89 [[ -z "$before_double_colon" ]] && num_before_double_colon=0
91 # Count the number of segments after "::"
92 local num_after_double_colon=$(echo "$after_double_colon" | grep -o ":" | wc -l)
93 [[ -z "$after_double_colon" ]] && num_after_double_colon=0
96 # Iterate over each segment
97 for segment in "${segments[@]}"; do
98 # If "::" is present, handle segments before and after it
99 if [[ "$double_colon_present" = true ]]; then
100 if [[ "$segment" = "" ]]; then
101 # Fill in the missing segments with "0000"
102 local missing_segments=$((8 - num_segments + 1))
103 for ((i=1; i<=$missing_segments; i++)); do
107 # Expand the segment to 4 characters and append it to the expanded IP
108 expanded_ip+=$(printf "%04s" "$segment" | tr ' ' '0')
112 # Expand the segment to 4 characters and append it to the expanded IP
113 expanded_ip+=$(printf "%04s" "$segment" | tr ' ' '0')
118 # Remove the trailing colon
119 expanded_ip="${expanded_ip%:}"
128 # Expand IP address and subnet mask to full length
129 ip=$(expand_ipv6 $ip)
131 local ip_blocks=(${ip//:/ })
132 local mask_blocks=(${mask//:/ })
135 for ((i = 0; i < 8; i++)); do
136 local dec_ip_block=$((16#${ip_blocks[i]}))
137 local dec_mask_block=$((16#${mask_blocks[i]}))
138 local network_block=$((dec_ip_block & dec_mask_block))
139 result+=$(printf "%04x" $network_block)
140 [ $i -lt 7 ] && result+=":"
145 # Check if the user wants to skip setting the routes
146 checkskipcmd=$(cat /sys/module/ksocklnd/parameters/skip_mr_route_setup 2>&-)
147 if [ "$checkskipcmd" == "1" ]; then
151 # Extract comma-separated interfaces from the argument
153 declare -a interfaces
154 for i in $(echo $1 | sed "s/,/ /g")
156 # verify that the interface exists
157 ipv4_addr=$(/sbin/ip -o -4 addr list $i 2>&- | awk '{print $4}' | cut -d/ -f1)
158 ipv6_addr=$(/sbin/ip -o -6 addr list $i 2>&- | awk '{print $4}' | cut -d/ -f1)
160 if [ -z "$ipv4_addr" ] && [ -z "$ipv6_addr" ]; then
161 # No IPv4 or IPv6 address configured on this interface, skip it
162 logcmd=(logger "${me}: skip setting up route for ${i}: IP address not found")
167 # Check if route is already set up for this interface (IPv4 or IPv6)
168 if [ ! -z "$ipv4_addr" ]; then
169 intfroute_ipv4=$(/sbin/ip -o -4 route show table $i 2>&-)
170 if [ ! -z "$intfroute_ipv4" ]; then
172 # IPv4 route exists, skip this interface
173 logcmd=(logger "${me}: skip setting up route for ${i}: IPv4 route exists")
179 if [ ! -z "$ipv6_addr" ]; then
180 intfroute_ipv6=$(/sbin/ip -o -6 route show table $i 2>&-)
181 if [ ! -z "$intfroute_ipv6" ]; then
182 # IPv6 route exists, skip this interface
183 logcmd=(logger "${me}: skip setting up route for ${i}: IPv6 route exists")
193 # this array will contain the interfaces
194 # already listed in rt_tables
197 # flush cache for every interface
198 for i in "${interfaces[@]}"
202 flushcmd=(/sbin/ip route flush table ${i} ${redirect} )
204 eval "${flushcmd[@]}"
205 logcmd=(logger "${me}: ${flushcmd[@]}")
209 filename='/etc/iproute2/rt_tables'
214 # trim leading and trailing spaces
215 line=`echo $line | sed -e 's/^[[:space:]]*//'`
216 linelen=$(echo -n $line | wc -m)
217 # don't check empty lines
218 if [ $linelen -lt 1 ]; then
221 # don't check comments
222 if [[ ${line:0:1} == "#" ]]; then
225 # split using space as separator
227 # check the table number and update the max
228 if [ $max_table_num -lt ${splitline[0]} ]; then
229 max_table_num=${splitline[0]}
231 # check if any of the interfaces are listed
232 for i in "${interfaces[@]}"
234 if [[ " ${splitline[@]} " =~ " ${i} " ]]; then
235 if [[ " ${interfaces[@]} " =~ " ${i} " ]]; then
236 interfaces_listed+=($i)
243 # add entries for unlisted interfaces
244 for i in "${interfaces[@]}"
246 if [[ ! " ${interfaces_listed[@]} " =~ " ${i} " ]]; then
247 max_table_num=$((max_table_num+1))
248 echo "$max_table_num $i" >> $filename
252 # generate list of available default gateways
253 gwsline=$(/sbin/ip route | awk '/default/ { print $3 }')
256 gwsline_ipv6=$(/sbin/ip -6 route | awk '/default/ { print $3 }')
257 gateways_ipv6=($gwsline_ipv6)
259 # Select a gateway on the same subnet for both IPv4 and IPv6
264 # Check if the IP address is IPv4 or IPv6
265 if [[ $ip =~ .*:.* ]]; then
267 ip=$(expand_ipv6 $ip)
268 mask=$(expand_ipv6 $mask)
269 local ip_blocks=(${ip//:/ })
270 local mask_blocks=(${mask//:/ })
273 for ((i = 0; i < 8; i++)); do
274 local dec_ip_block=$((16#${ip_blocks[i]}))
275 local dec_mask_block=$((16#${mask_blocks[i]}))
276 local network_block=$((dec_ip_block & dec_mask_block))
277 result+=$(printf "%04x" $network_block)
278 [ $i -lt 7 ] && result+=":"
281 local network_ipv6=$result
282 for gw in "${gateways_ipv6[@]}"; do
283 gw_network=$(netcalcipv6 "$gw" "$mask")
284 if [[ "$network_ipv6" == "$gw_network" ]]; then
291 local ip_parts=(${ip//./ })
292 local mask_parts=(${mask//./ })
293 local network_ipv4=""
295 for ((i = 0; i < 4; i++)); do
296 local network_part=$((ip_parts[i] & mask_parts[i]))
297 network_ipv4+="$network_part"
298 [ $i -lt 3 ] && network_ipv4+="."
301 for gw in "${gateways[@]}"; do
302 gw_network=$(netcalc "$gw" "$mask")
303 if [[ "$network_ipv4" == "$gw_network" ]]; then
312 # Add the routing entries and rules for IPv4 and/or IPv6
313 for i in "${interfaces[@]}"
315 # Extract IPv4 and IPv6 addresses and netmasks in CIDR format
316 addr_ipv4=($(/sbin/ip -o -4 addr list $i 2>&- | awk '{print $4}' | cut -d/ -f1))
317 cidrmask_ipv4=($(/sbin/ip -o -4 addr list $i 2>&- | awk '{print $4}' | cut -d/ -f2))
318 addr_ipv6=($(/sbin/ip -o -6 addr list $i 2>&- | awk '{print $4}' | cut -d/ -f1))
319 cidrmask_ipv6=($(/sbin/ip -o -6 addr list $i 2>&- | awk '{print $4}' | cut -d/ -f2))
320 # Configure routing and rules for IPv4 (if IPv4 address is configured)
321 if [ ! -z "${addr_ipv4}" ]; then
322 # Convert CIDR mask to mask in dot format for IPv4
323 dotmask_ipv4=$(cidr2mask ${cidrmask_ipv4[0]})
324 # Find a gateway on the same subnet for IPv4
325 gw_ipv4=$(selectgw "${addr_ipv4[0]}" "$dotmask_ipv4")
326 # Build and execute route commands for IPv4
327 if [[ $gw_ipv4 == "0.0.0.0" ]]; then
328 # Gateway not found, assume local destinations for IPv4
329 net_ipv4=$(netcalc "${addr_ipv4[0]}" "$dotmask_ipv4")
330 routecmd_ipv4=(/sbin/ip route add ${net_ipv4}/${cidrmask_ipv4[0]} dev ${i} proto kernel scope link src ${addr_ipv4[0]} table ${i})
332 routecmd_ipv4=(/sbin/ip route add default via ${gw_ipv4} dev ${i} table ${i})
334 ruledelcmd_ipv4=(/sbin/ip rule del from ${addr_ipv4[0]} table ${i} '&>/dev/null')
335 ruleaddcmd_ipv4=(/sbin/ip rule add from ${addr_ipv4[0]} table ${i})
337 routeerr_ipv4=$(eval "${routecmd_ipv4[@]}" 2>&1 >/dev/null)
338 ruledelerr_ipv4=$(eval "${ruledelcmd_ipv4[@]}" 2>&1 >/dev/null)
339 ruleadderr_ipv4=$(eval "${ruleaddcmd_ipv4[@]}" 2>&1 >/dev/null)
341 logcmd1_ipv4=(logger "${me}: ${routecmd_ipv4[@]} ${routeerr_ipv4}")
342 logcmd2_ipv4=(logger "${me}: ${ruledelcmd_ipv4[@]} ${ruledelerr_ipv4}")
343 logcmd3_ipv4=(logger "${me}: ${ruleaddcmd_ipv4[@]} ${ruleadderr_ipv4}")
345 eval "${logcmd1_ipv4[@]}"
346 eval "${logcmd2_ipv4[@]}"
347 eval "${logcmd3_ipv4[@]}"
350 # Configure routing and rules for IPv6 (if IPv6 address is configured)
351 if [ ! -z "${addr_ipv6}" ]; then
352 # Convert CIDR mask to mask in dot format for IPv6
353 dotmask_ipv6=$(cidr2maskipv6 ${cidrmask_ipv6[0]})
354 # Find a gateway on the same subnet for IPv6
355 gw_ipv6=$(selectgw "${addr_ipv6[0]}" "$dotmask_ipv6")
356 # Build and execute route commands for IPv6
357 if [[ $gw_ipv6 == "0.0.0.0" ]]; then
358 # Gateway not found, assume local destinations for IPv6
359 net_ipv6=$(netcalcipv6 "${addr_ipv6[0]}" "$dotmask_ipv6")
360 routecmd_ipv6=(/sbin/ip -6 route add ${addr_ipv6[0]}/${cidrmask_ipv6[0]} dev ${i} proto kernel scope link src ${addr_ipv6[0]} table ${i})
362 routecmd_ipv6=(/sbin/ip -6 route add default via ${gw_ipv6} dev ${i} table ${i})
364 ruledelcmd_ipv6=(/sbin/ip -6 rule del from ${addr_ipv6[0]} table ${i} '&>/dev/null')
365 ruleaddcmd_ipv6=(/sbin/ip -6 rule add from ${addr_ipv6[0]} table ${i})
367 routeerr_ipv6=$(eval "${routecmd_ipv6[@]}" 2>&1 >/dev/null)
368 ruledelerr_ipv6=$(eval "${ruledelcmd_ipv6[@]}" 2>&1 >/dev/null)
369 ruleadderr_ipv6=$(eval "${ruleaddcmd_ipv6[@]}" 2>&1 >/dev/null)
371 logcmd1_ipv6=(logger -- "${me}: ${routecmd_ipv6[@]} ${routeerr_ipv6}")
372 logcmd2_ipv6=(logger -- "${me}: ${ruledelcmd_ipv6[@]} ${ruledelerr_ipv6}")
373 logcmd3_ipv6=(logger -- "${me}: ${ruleaddcmd_ipv6[@]} ${ruleadderr_ipv6}")
375 eval "${logcmd1_ipv6[@]}"
376 eval "${logcmd2_ipv6[@]}"
377 eval "${logcmd3_ipv6[@]}"
382 for i in "${interfaces[@]}"
384 flushcmd=(/sbin/ip neigh flush dev ${i})