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