Whamcloud - gitweb
LU-17888 osd-ldiskfs: osd_scrub_refresh_mapping deadlock
[fs/lustre-release.git] / lustre / scripts / ksocklnd-config
1 #!/bin/bash
2
3 me="${0##*/}"
4
5 # convert number of mask bits to x.x.x.x or x:x:x:x:x:x:x:x mask format
6 cidr2mask() {
7         local i mask=""
8         local full_octets=$(($1/8))
9         local partial_octet=$(($1%8))
10
11         for ((i=0;i<4;i+=1)); do
12                 if [ $i -lt $full_octets ]; then
13                         mask+=255
14                 elif [ $i -eq $full_octets ]; then
15                         mask+=$((256 - 2**(8-$partial_octet)))
16                 else
17                         mask+=0
18                 fi
19                 test $i -lt 3 && mask+=.
20         done
21         echo $mask
22 }
23
24 cidr2maskipv6() {
25         local mask=""
26         local num_bits=$1
27
28         if [ $num_bits -le 128 ]; then
29                 local full_blocks=$((num_bits / 16))
30                 local remaining_bits=$((16 - (num_bits % 16)))
31
32                 for ((i = 0; i < 8; i++)); do
33                         if [ $i -lt $full_blocks ]; then
34                                 mask+="ffff"
35                         elif [ $i -eq $full_blocks ]; then
36                                 mask+="$(printf "%x" $((0xFFFF >> $remaining_bits)))"
37                         else
38                                 mask+="0"
39                         fi
40                         [ $i -lt 7 ] && mask+=":"
41                 done
42         else
43                 echo "Invalid prefix length for IPv6"
44                 return 1
45         fi
46         echo $mask
47 }
48
49 # apply netmask (second argument) to ip address (first argument)
50 netcalc() {
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"
64 }
65
66 # expand IPv6 address to full length
67 expand_ipv6() {
68         local ip=$1
69         local expanded_ip=""
70
71         # Split the IP address into segments
72         IFS=':' read -r -a segments <<< "$ip"
73
74         # Count the number of segments
75         local num_segments=${#segments[@]}
76
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
80
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]}
86
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
90
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
94         fi
95
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
104                                         expanded_ip+="0000:"
105                                 done
106                         else
107                                 # Expand the segment to 4 characters and append it to the expanded IP
108                                 expanded_ip+=$(printf "%04s" "$segment" | tr ' ' '0')
109                                 expanded_ip+=":"
110                         fi
111                 else
112                         # Expand the segment to 4 characters and append it to the expanded IP
113                         expanded_ip+=$(printf "%04s" "$segment" | tr ' ' '0')
114                         expanded_ip+=":"
115                 fi
116         done
117
118         # Remove the trailing colon
119         expanded_ip="${expanded_ip%:}"
120
121         echo "$expanded_ip"
122 }
123
124 netcalcipv6() {
125         local ip=$1
126         local mask=$2
127
128         # Expand IP address and subnet mask to full length
129         ip=$(expand_ipv6 $ip)
130
131         local ip_blocks=(${ip//:/ })
132         local mask_blocks=(${mask//:/ })
133         local result=""
134
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+=":"
141         done
142         echo $result
143 }
144
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
148         exit 0
149 fi
150
151 # Extract comma-separated interfaces from the argument
152 j=0
153 declare -a interfaces
154 for i in $(echo $1 | sed "s/,/ /g")
155 do
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)
159
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")
163                 eval "${logcmd[@]}"
164                 continue
165         fi
166
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
171                         echo $intfroute_ipv4
172                         # IPv4 route exists, skip this interface
173                         logcmd=(logger "${me}: skip setting up route for ${i}: IPv4 route exists")
174                         eval "${logcmd[@]}"
175                         continue
176                 fi
177         fi
178
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")
184                         eval "${logcmd[@]}"
185                         continue
186                 fi
187         fi
188
189         interfaces[$j]=$i
190         j=$((j+1))
191 done
192
193 # this array will contain the interfaces
194 # already listed in rt_tables
195 interfaces_listed=()
196
197 # flush cache for every interface
198 for i in "${interfaces[@]}"
199 do
200         # build command
201         redirect="2>&-"
202         flushcmd=(/sbin/ip route flush table ${i} ${redirect} )
203         # execute command
204         eval "${flushcmd[@]}"
205         logcmd=(logger "${me}: ${flushcmd[@]}")
206         eval "${logcmd[@]}"
207 done
208
209 filename='/etc/iproute2/rt_tables'
210 n=1
211 max_table_num=0
212 while read line; do
213         # reading each line
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
219                 continue
220         fi
221         # don't check comments
222         if [[ ${line:0:1} == "#" ]]; then
223                 continue
224         fi
225         # split using space as separator
226         splitline=( $line )
227         # check the table number and update the max
228         if [ $max_table_num -lt ${splitline[0]} ]; then
229                 max_table_num=${splitline[0]}
230         fi
231         # check if any of the interfaces are listed
232         for i in "${interfaces[@]}"
233         do
234                 if [[ " ${splitline[@]} " =~ " ${i} " ]]; then
235                         if [[ " ${interfaces[@]} " =~ " ${i} " ]]; then
236                                 interfaces_listed+=($i)
237                         fi
238                 fi
239         done
240         n=$((n+1))
241 done < $filename
242
243 # add entries for unlisted interfaces
244 for i in "${interfaces[@]}"
245 do
246         if [[ ! " ${interfaces_listed[@]} " =~ " ${i} " ]]; then
247                 max_table_num=$((max_table_num+1))
248                 echo "$max_table_num $i" >> $filename
249         fi
250 done
251
252 # generate list of available default gateways
253 gwsline=$(/sbin/ip route | awk '/default/ { print $3 }')
254 gateways=($gwsline)
255
256 gwsline_ipv6=$(/sbin/ip -6 route | awk '/default/ { print $3 }')
257 gateways_ipv6=($gwsline_ipv6)
258
259 # Select a gateway on the same subnet for both IPv4 and IPv6
260 selectgw() {
261         local ip=$1
262         local mask=$2
263
264         # Check if the IP address is IPv4 or IPv6
265         if [[ $ip =~ .*:.* ]]; then
266                 # IPv6
267                 ip=$(expand_ipv6 $ip)
268                 mask=$(expand_ipv6 $mask)
269                 local ip_blocks=(${ip//:/ })
270                 local mask_blocks=(${mask//:/ })
271                 local result=""
272
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+=":"
279                 done
280
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
285                                 echo $gw
286                                 return
287                         fi
288                 done
289         else
290                 # IPv4
291                 local ip_parts=(${ip//./ })
292                 local mask_parts=(${mask//./ })
293                 local network_ipv4=""
294
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+="."
299                 done
300
301                 for gw in "${gateways[@]}"; do
302                         gw_network=$(netcalc "$gw" "$mask")
303                         if [[ "$network_ipv4" == "$gw_network" ]]; then
304                                 echo $gw
305                                 return
306                         fi
307                 done
308         fi
309         echo "0.0.0.0"
310 }
311
312 # Add the routing entries and rules for IPv4 and/or IPv6
313 for i in "${interfaces[@]}"
314 do
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})
331                 else
332                         routecmd_ipv4=(/sbin/ip route add default via ${gw_ipv4} dev ${i} table ${i})
333                 fi
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})
336
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)
340
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}")
344
345                 eval "${logcmd1_ipv4[@]}"
346                 eval "${logcmd2_ipv4[@]}"
347                 eval "${logcmd3_ipv4[@]}"
348         fi
349
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})
361                 else
362                         routecmd_ipv6=(/sbin/ip -6 route add default via ${gw_ipv6} dev ${i} table ${i})
363                 fi
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})
366
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)
370
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}")
374
375                 eval "${logcmd1_ipv6[@]}"
376                 eval "${logcmd2_ipv6[@]}"
377                 eval "${logcmd3_ipv6[@]}"
378         fi
379 done
380
381 # flush arp tables
382 for i in "${interfaces[@]}"
383 do
384         flushcmd=(/sbin/ip neigh flush dev ${i})
385         eval ${flushcmd[@]}
386 done
387