ctdb-tests: Move variable FAKE_IP_STATE into the ip stub
[samba.git] / ctdb / tests / eventscripts / stubs / ip
1 #!/bin/sh
2
3 FAKE_IP_STATE="${FAKE_NETWORK_STATE}/ip-state"
4 mkdir -p "$FAKE_IP_STATE"
5
6 promote_secondaries=true
7
8 not_implemented ()
9 {
10     echo "ip stub command: \"$1\" not implemented"
11     exit 127
12 }
13
14 ######################################################################
15
16 ip_link ()
17 {
18     case "$1" in
19         set)
20             shift
21             # iface="$1"
22             case "$2" in
23                 up)   ip_link_set_up "$1"  ;;
24                 down) ip_link_down_up "$1" ;;
25                 *)    not_implemented "\"$2\" in \"$orig_args\"" ;;
26             esac
27             ;;
28         show) shift ; ip_link_show "$@" ;;
29         add*) shift ; ip_link_add "$@" ;;
30         del*) shift ; ip_link_delete "$@" ;;
31         *) not_implemented "$*" ;;
32     esac
33 }
34
35 ip_link_add ()
36 {
37     _link=""
38     _name=""
39     _type=""
40
41     while [ -n "$1" ] ; do
42         case "$1" in
43             link)
44                 _link="$2"
45                 shift 2
46                 ;;
47             name)
48                 _name="$2"
49                 shift 2
50                 ;;
51             type)
52                 if [ "$2" != "vlan" ] ; then
53                     not_implemented "link type $1"
54                 fi
55                 _type="$2"
56                 shift 2
57                 ;;
58             id) shift 2 ;;
59             *) not_implemented "$1" ;;
60         esac
61     done
62
63     case "$_type" in
64         vlan)
65             if [ -z "$_name" -o -z "$_link" ] ; then
66                 not_implemented "ip link add with null name or link"
67             fi
68
69             mkdir -p "${FAKE_IP_STATE}/interfaces-vlan"
70             echo "$_link" >"${FAKE_IP_STATE}/interfaces-vlan/${_name}"
71             ip_link_set_down "$_name"
72             ;;
73     esac
74 }
75
76 ip_link_delete ()
77 {
78     mkdir -p "${FAKE_IP_STATE}/interfaces-deleted"
79     touch "${FAKE_IP_STATE}/interfaces-deleted/$1"
80     rm -f "${FAKE_IP_STATE}/interfaces-vlan/$1"
81 }
82
83 ip_link_set_up ()
84 {
85     rm -f "${FAKE_IP_STATE}/interfaces-down/$1"
86     rm -f "${FAKE_IP_STATE}/interfaces-deleted/$1"
87 }
88
89 ip_link_set_down ()
90 {
91     rm -f "${FAKE_IP_STATE}/interfaces-deleted/$1"
92     mkdir -p "${FAKE_IP_STATE}/interfaces-down"
93     touch "${FAKE_IP_STATE}/interfaces-down/$1"
94 }
95
96 ip_link_show ()
97 {
98     dev="$1"
99     if [ "$dev" = "dev" -a -n "$2" ] ; then
100         dev="$2"
101     fi
102
103     if [ -e "${FAKE_IP_STATE}/interfaces-deleted/$dev" ] ; then
104         echo "Device \"${dev}\" does not exist." >&2
105         exit 255
106     fi
107
108     if [ -r "${FAKE_IP_STATE}/interfaces-vlan/${dev}" ] ; then
109         read _link <"${FAKE_IP_STATE}/interfaces-vlan/${dev}"
110         dev="${dev}@${_link}"
111     fi
112
113     _state="UP"
114     _flags=",UP,LOWER_UP"
115     if [ -e "${FAKE_IP_STATE}/interfaces-down/$dev" ] ; then
116         _state="DOWN"
117         _flags=""
118     fi
119     case "$dev" in
120     lo)
121             _mac="00:00:00:00:00:00"
122             _brd="00:00:00:00:00:00"
123             _type="loopback"
124             _opts="<LOOPBACK${_flags}> mtu 65536 qdisc noqueue state UNKNOWN"
125             ;;
126     *)
127             _mac=$(echo $dev | md5sum | sed -r -e 's@(..)(..)(..)(..)(..)(..).*@\1:\2:\3:\4:\5:\6@')
128             _brd="ff:ff:ff:ff:ff:ff"
129             _type="ether"
130             _opts="<BROADCAST,MULTICAST${_flags}> mtu 1500 qdisc pfifo_fast state ${_state} qlen 1000"
131     esac
132     echo "${n:-42}: ${dev}: ${_opts}"
133     echo "    link/${_type} ${_mac} brd ${_brd}"
134 }
135
136 # This is incomplete because it doesn't actually look up table ids in
137 # /etc/iproute2/rt_tables.  The rules/routes are actually associated
138 # with the name instead of the number.  However, we include a variable
139 # to fake a bad table id.
140 [ -n "$IP_ROUTE_BAD_TABLE_ID" ] || IP_ROUTE_BAD_TABLE_ID=false
141
142 ip_check_table ()
143 {
144     _cmd="$1"
145
146     if [ "$_cmd" = "route" -a -z "$_table" ] ;then
147         _table="main"
148     fi
149
150     [ -n "$_table" ] || not_implemented "ip rule/route without \"table\""
151
152     # Only allow tables names from 13.per_ip_routing and "main".  This
153     # is a cheap way of avoiding implementing the default/local
154     # tables.
155     case "$_table" in
156         ctdb.*|main)
157             if $IP_ROUTE_BAD_TABLE_ID ; then
158                 # Ouch.  Simulate inconsistent errors from ip.  :-(
159                 case "$_cmd" in
160                     route)
161                         echo "Error: argument "${_table}" is wrong: table id value is invalid" >&2
162                         
163                         ;;
164                     *)
165                         echo "Error: argument "${_table}" is wrong: invalid table ID" >&2
166                 esac
167                 exit 255
168             fi
169             ;;
170         *) not_implemented "table=${_table} ${orig_args}" ;;
171     esac
172 }
173
174 ######################################################################
175
176 ip_addr ()
177 {
178     case "$1" in
179         show|list|"") shift ; ip_addr_show "$@" ;;
180         add*)         shift ; ip_addr_add  "$@" ;;
181         del*)         shift ; ip_addr_del  "$@" ;;
182         *) not_implemented "\"$1\" in \"$orig_args\"" ;;
183     esac
184 }
185
186 ip_addr_show ()
187 {
188     dev=""
189     primary=true
190     secondary=true
191     _to=""
192     while [ -n "$1" ] ; do
193         case "$1" in
194             dev)
195                 dev="$2" ; shift 2
196                 ;;
197             # Do stupid things and stupid things will happen!
198             primary)
199                 primary=true ; secondary=false ; shift
200                 ;;
201             secondary)
202                 secondary=true ; primary=false ; shift
203                 ;;
204             to)
205                 _to="$2" ; shift 2
206                 ;;
207             *)
208                 # Assume an interface name
209                 dev="$1" ; shift 1
210         esac
211     done
212     devices="$dev"
213     if [ -z "$devices" ] ; then
214         # No device specified?  Get all the primaries...
215         devices=$(ls "${FAKE_IP_STATE}/addresses/"*-primary 2>/dev/null | \
216             sed -e 's@.*/@@' -e 's@-.*-primary$@@' | sort -u)
217     fi
218     calc_brd ()
219     {
220             case "${local#*/}" in
221             24) brd="${local%.*}.255" ;;
222             32) brd="" ;;
223             *) not_implemented "list ... fake bits other than 24/32: ${local#*/}"
224         esac
225     }
226     show_iface()
227     {
228         ip_link_show "$dev"
229
230         nets=$(ls "${FAKE_IP_STATE}/addresses/${dev}"-*-primary 2>/dev/null | \
231             sed -e 's@.*/@@' -e "s@${dev}-\(.*\)-primary\$@\1@")
232
233         for net in $nets ; do
234             pf="${FAKE_IP_STATE}/addresses/${dev}-${net}-primary"
235             sf="${FAKE_IP_STATE}/addresses/${dev}-${net}-secondary"
236             if $primary && [ -r "$pf" ] ; then
237                 read local scope <"$pf"
238                 if [ -z "$_to" -o "${_to%/*}" = "${local%/*}" ] ; then
239                     calc_brd
240                     echo "    inet ${local} ${brd:+brd ${brd} }scope ${scope} ${dev}"
241                 fi
242             fi
243             if $secondary && [ -r "$sf" ] ; then
244                 while read local scope ; do
245                     if [ -z "$_to" -o "${_to%/*}" = "${local%/*}" ] ; then
246                         calc_brd
247                         echo "    inet ${local} ${brd:+brd }${brd} scope ${scope} secondary ${dev}"
248                     fi
249                 done <"$sf"
250             fi
251             if [ -z "$_to" ] ; then
252                 echo "       valid_lft forever preferred_lft forever"
253             fi
254         done
255     }
256     n=1
257     for dev in $devices ; do
258         if [ -z "$_to" ] || \
259             grep -F "${_to%/*}/" "${FAKE_IP_STATE}/addresses/${dev}-"* >/dev/null ; then
260             show_iface
261         fi
262         n=$(($n + 1))
263     done
264 }
265
266 # Copied from 13.per_ip_routing for now... so this is lazy testing  :-(
267 ipv4_host_addr_to_net ()
268 {
269     _host="$1"
270     _maskbits="$2"
271
272     # Convert the host address to an unsigned long by splitting out
273     # the octets and doing the math.
274     _host_ul=0
275     for _o in $(export IFS="." ; echo $_host) ; do
276         _host_ul=$(( ($_host_ul << 8) + $_o)) # work around Emacs color bug
277     done
278
279     # Calculate the mask and apply it.
280     _mask_ul=$(( 0xffffffff << (32 - $_maskbits) ))
281     _net_ul=$(( $_host_ul & $_mask_ul ))
282
283     # Now convert to a network address one byte at a time.
284     _net=""
285     for _o in $(seq 1 4) ; do
286         _net="$(($_net_ul & 255))${_net:+.}${_net}"
287         _net_ul=$(($_net_ul >> 8))
288     done
289
290     echo "${_net}/${_maskbits}"
291 }
292
293 ip_addr_add ()
294 {
295     local=""
296     dev=""
297     brd=""
298     scope="global"
299     while [ -n "$1" ] ; do
300         case "$1" in
301             *.*.*.*/*)
302                 local="$1" ; shift
303                 ;;
304             local)
305                 local="$2" ; shift 2
306                 ;;
307             broadcast|brd)
308                 # For now assume this is always '+'.
309                 if [ "$2" != "+" ] ; then
310                     not_implemented "addr add ... brd $2 ..."
311                 fi
312                 shift 2
313                 ;;
314             dev)
315                 dev="$2" ; shift 2
316                 ;;
317             scope)
318                 scope="$2" ; shift 2
319                 ;;
320             *)
321                 not_implemented "$@"
322         esac
323     done
324     if [ -z "$dev" ] ; then
325         not_implemented "addr add (without dev)"
326     fi
327     mkdir -p "${FAKE_IP_STATE}/addresses"
328     net_str=$(ipv4_host_addr_to_net $(IFS="/" ; echo $local))
329     net_str=$(echo "$net_str" | sed -e 's@/@_@')
330     pf="${FAKE_IP_STATE}/addresses/${dev}-${net_str}-primary"
331     sf="${FAKE_IP_STATE}/addresses/${dev}-${net_str}-secondary"
332     # We could lock here... but we should be the only ones playing
333     # around here with these stubs.
334     if [ ! -f "$pf" ] ; then
335         echo "$local $scope" >"$pf"
336     elif grep -Fq "$local" "$pf" ; then 
337         echo "RTNETLINK answers: File exists" >&2
338         exit 254
339     elif [ -f "$sf" ] && grep -Fq "$local" "$sf" ; then 
340         echo "RTNETLINK answers: File exists" >&2
341         exit 254
342     else
343         echo "$local $scope" >>"$sf"
344     fi
345 }
346
347 ip_addr_del ()
348 {
349     local=""
350     dev=""
351     while [ -n "$1" ] ; do
352         case "$1" in
353             *.*.*.*/*)
354                 local="$1" ; shift
355                 ;;
356             local)
357                 local="$2" ; shift 2
358                 ;;
359             dev)
360                 dev="$2" ; shift 2
361                 ;;
362             *)
363                 not_implemented "addr del ... $1 ..."
364         esac
365     done
366     if [ -z "$dev" ] ; then
367         not_implemented "addr del (without dev)"
368     fi
369     mkdir -p "${FAKE_IP_STATE}/addresses"
370     net_str=$(ipv4_host_addr_to_net $(IFS="/" ; echo $local))
371     net_str=$(echo "$net_str" | sed -e 's@/@_@')
372     pf="${FAKE_IP_STATE}/addresses/${dev}-${net_str}-primary"
373     sf="${FAKE_IP_STATE}/addresses/${dev}-${net_str}-secondary"
374     # We could lock here... but we should be the only ones playing
375     # around here with these stubs.
376     if [ ! -f "$pf" ] ; then
377         echo "RTNETLINK answers: Cannot assign requested address" >&2
378         exit 254
379     elif grep -Fq "$local" "$pf" ; then
380         if $promote_secondaries && [ -s "$sf" ] ; then
381             head -n 1 "$sf" >"$pf"
382             sed -i -e '1d' "$sf"
383         else
384             # Remove primaries AND SECONDARIES.
385             rm -f "$pf" "$sf"
386         fi
387     elif [ -f "$sf" ] && grep -Fq "$local" "$sf" ; then 
388         grep -Fv "$local" "$sf" >"${sf}.new"
389         mv "${sf}.new" "$sf"
390     else
391         echo "RTNETLINK answers: Cannot assign requested address" >&2
392         exit 254
393     fi
394 }
395
396 ######################################################################
397
398 ip_rule ()
399 {
400     case "$1" in
401         show|list|"") shift ; ip_rule_show "$@" ;;
402         add)          shift ; ip_rule_add  "$@" ;;
403         del*)         shift ; ip_rule_del  "$@" ;;
404         *) not_implemented "$1 in \"$orig_args\"" ;;
405     esac
406
407 }
408
409 # All non-default rules are in $FAKE_IP_STATE_RULES/rules.  As with
410 # the real version, rules can be repeated.  Deleting just deletes the
411 # 1st match.
412
413 ip_rule_show ()
414 {
415     ip_rule_show_1 ()
416     {
417         _pre="$1"
418         _table="$2"
419         _selectors="$3"
420         # potentially more options
421
422         printf "%d:\t%s lookup %s \n" $_pre "$_selectors" "$_table"
423     }
424
425     ip_rule_show_some ()
426     {
427         _min="$1"
428         _max="$2"
429
430         [ -f "${FAKE_IP_STATE}/rules" ] || return
431
432         while read _pre _table _selectors ; do
433             # Only print those in range
434             [ $_min -le $_pre -a $_pre -le $_max ] || continue
435
436             ip_rule_show_1 $_pre "$_table" "$_selectors"
437         done <"${FAKE_IP_STATE}/rules"
438     }
439
440     ip_rule_show_1 0 "local" "from all"
441
442     ip_rule_show_some 1 32765
443
444     ip_rule_show_1 32766 "main" "from all"
445     ip_rule_show_1 32767 "default" "from all"
446
447     ip_rule_show_some 32768 2147483648
448 }
449
450 ip_rule_common ()
451 {
452     _from=""
453     _pre=""
454     _table=""
455     while [ -n "$1" ] ; do
456         case "$1" in
457             from)  _from="$2"  ; shift 2 ;;
458             pref)  _pre="$2"   ; shift 2 ;;
459             table) _table="$2" ; shift 2 ;;
460             *) not_implemented "$1 in \"$orig_args\"" ;;
461         esac
462     done
463
464     [ -n "$_pre" ]   || not_implemented "ip rule without \"pref\""
465     ip_check_table "rule"
466     # Relax this if more selectors added later...
467     [ -n "$_from" ]  || not_implemented "ip rule without \"from\""
468 }
469
470 ip_rule_add ()
471 {
472     ip_rule_common "$@"
473
474     _f="${FAKE_IP_STATE}/rules"
475     touch "$_f"
476     (
477         flock 0
478         # Filter order must be consistent with the comparison in ip_rule_del()
479         echo "$_pre $_table${_from:+ from }$_from" >>"$_f"
480     ) <"$_f"
481 }
482
483 ip_rule_del ()
484 {
485     ip_rule_common "$@"
486
487     _f="${FAKE_IP_STATE}/rules"
488     touch "$_f"
489     (
490         flock 0
491         _tmp="${_f}.new"
492         : >"$_tmp"
493         _found=false
494         while read _p _t _s ; do
495             if ! $_found && \
496                 [ "$_p" = "$_pre" -a "$_t" = "$_table" -a \
497                 "$_s" = "${_from:+from }$_from" ] ; then
498                 # Found.  Skip this one but not future ones.
499                 _found=true
500             else
501                 echo "$_p $_t $_s" >>"$_tmp"
502             fi
503         done
504         if cmp -s "$_tmp" "$_f" ; then
505             # No changes, must not have found what we wanted to delete
506             echo "RTNETLINK answers: No such file or directory" >&2
507             rm -f "$_tmp"
508             exit 2
509         else
510             mv "$_tmp" "$_f"
511         fi
512     ) <"$_f" || exit $?
513 }
514
515 ######################################################################
516
517 ip_route ()
518 {
519     case "$1" in
520         show|list)    shift ; ip_route_show  "$@" ;;
521         flush)        shift ; ip_route_flush "$@" ;;
522         add)          shift ; ip_route_add   "$@" ;;
523         del*)         shift ; ip_route_del   "$@" ;;
524         *) not_implemented "$1 in \"ip route\"" ;;
525     esac
526 }
527
528 ip_route_common ()
529 {
530     if [ "$1" = table ] ; then
531         _table="$2"
532         shift 2
533     fi
534
535     ip_check_table "route"
536 }
537
538 # Routes are in a file per table in the directory
539 # $FAKE_IP_STATE/routes.  These routes just use the table ID
540 # that is passed and don't do any lookup.  This could be "improved" if
541 # necessary.
542
543 ip_route_show ()
544 {
545     ip_route_common "$@"
546
547     # Missing file is just an empty table
548     sort "$FAKE_IP_STATE/routes/${_table}" 2>/dev/null || true
549 }
550
551 ip_route_flush ()
552 {
553     ip_route_common "$@"
554
555     rm -f "$FAKE_IP_STATE/routes/${_table}"
556 }
557
558 ip_route_add ()
559 {
560     _prefix=""
561     _dev=""
562     _gw=""
563     _table=""
564     _metric=""
565
566     while [ -n "$1" ] ; do
567         case "$1" in
568             *.*.*.*/*|*.*.*.*) _prefix="$1" ; shift 1 ;;
569             local) _prefix="$2" ; shift 2 ;;
570             dev)   _dev="$2"   ; shift 2 ;;
571             via)   _gw="$2"    ; shift 2 ;;
572             table) _table="$2" ; shift 2 ;;
573             metric) _metric="$2" ; shift 2 ;;
574             *) not_implemented "$1 in \"$orig_args\"" ;;
575         esac
576     done
577
578     ip_check_table "route"
579     [ -n "$_prefix" ] || not_implemented "ip route without inet prefix in \"$orig_args\""
580     # This can't be easily deduced, so print some garbage.
581     [ -n "$_dev" ] || _dev="ethXXX"
582
583     # Alias or add missing bits
584     case "$_prefix" in
585         0.0.0.0/0) _prefix="default" ;;
586         */*) : ;;
587         *) _prefix="${_prefix}/32" ;;
588     esac
589
590     _f="$FAKE_IP_STATE/routes/${_table}"
591     mkdir -p "$FAKE_IP_STATE/routes"
592     touch "$_f"
593
594     # Check for duplicate
595     _prefix_regexp=$(echo "^${_prefix}" | sed -e 's@\.@\\.@g')
596     if [ -n "$_metric" ] ; then
597         _prefix_regexp="${_prefix_regexp} .*metric ${_metric} "
598     fi
599     if grep -q "$_prefix_regexp" "$_f" ; then
600         echo "RTNETLINK answers: File exists" >&2
601         exit 1
602     fi
603
604     (
605         flock 0
606
607         _out="${_prefix} "
608         [ -z "$_gw" ] || _out="${_out}via ${_gw} "
609         [ -z "$_dev" ] || _out="${_out}dev ${_dev} "
610         [ -n "$_gw" ] || _out="${_out} scope link "
611         [ -z "$_metric" ] || _out="${_out} metric ${_metric} "
612         echo "$_out" >>"$_f"
613     ) <"$_f"
614 }
615
616 ip_route_del ()
617 {
618     _prefix=""
619     _dev=""
620     _gw=""
621     _table=""
622     _metric=""
623
624     while [ -n "$1" ] ; do
625         case "$1" in
626             *.*.*.*/*|*.*.*.*) _prefix="$1" ; shift 1 ;;
627             local) _prefix="$2" ; shift 2 ;;
628             dev)   _dev="$2"   ; shift 2 ;;
629             via)   _gw="$2"    ; shift 2 ;;
630             table) _table="$2" ; shift 2 ;;
631             metric) _metric="$2" ; shift 2 ;;
632             *) not_implemented "$1 in \"$orig_args\"" ;;
633         esac
634     done
635
636     ip_check_table "route"
637     [ -n "$_prefix" ] || not_implemented "ip route without inet prefix in \"$orig_args\""
638     # This can't be easily deduced, so print some garbage.
639     [ -n "$_dev" ] || _dev="ethXXX"
640
641     # Alias or add missing bits
642     case "$_prefix" in
643         0.0.0.0/0) _prefix="default" ;;
644         */*) : ;;
645         *) _prefix="${_prefix}/32" ;;
646     esac
647
648     _f="$FAKE_IP_STATE/routes/${_table}"
649     mkdir -p "$FAKE_IP_STATE/routes"
650     touch "$_f"
651
652     (
653         flock 0
654
655         # Escape some dots
656         [ -z "$_gw" ] || _gw=$(echo "$_gw" | sed -e 's@\.@\\.@g')
657         _prefix=$(echo "$_prefix" | sed -e 's@\.@\\.@g' -e 's@/@\\/@')
658
659         _re="^${_prefix}\>.*"
660         [ -z "$_gw" ] || _re="${_re}\<via ${_gw}\>.*"
661         [ -z "$_dev" ] || _re="${_re}\<dev ${_dev}\>.*"
662         [ -z "$_metric" ] || _re="${_re}.*\<metric ${_metric}\>.*"
663         sed -i -e "/${_re}/d" "$_f"
664     ) <"$_f"
665 }
666
667 ######################################################################
668
669 orig_args="$*"
670
671 case "$1" in
672     link)   shift ; ip_link  "$@" ;;
673     addr*)  shift ; ip_addr  "$@" ;;
674     rule)   shift ; ip_rule  "$@" ;;
675     route)  shift ; ip_route "$@" ;;
676     *) not_implemented "$1" ;;
677 esac
678
679 exit 0