50fea9d8b228311af565ee5c420a6e05496a8122
[racktables] / gateways / switchvlans / main
1 #!/bin/sh
2
3 # This is a RackTables gateway for changing switch ports membership
4 # across VLANs. It works accordingly to the gateway protocol described
5 # in gateways.php and accepts the following commands on its stdin:
6 # * listvlans: list all VLANs found on the switch, propably filtering
7 # out those administratively prohibited. Only the VLANs from this
8 # list will be allowed as new destination for 'set' command.
9 # * listports: list all ports on the switch and their current status.
10 # Untagged (switchport mode access) ports will be shown with their
11 # VLAN ID and tagged ports will be shown as 'trunk' regardless of
12 # how many VLANs they are members of.
13
14 endpoint=
15 hw=
16 sw=
17 user=
18 CONNECTED=0
19
20
21 authorized()
22 {
23 local endp=$1 user=$2 action=$3 arg1=$4 arg2=$5 skip=yes cval
24 [ -z "$endp" -o -z "$user" -o -z "$action" ] && return 1
25
26 # Now we strip PHP wrapping(s) and process auth rules only.
27 # Accept more than one ruleset on the floor.
28 while read line; do
29 if [ "$skip" = "yes" -a "$line" = "# S-T-A-R-T" ]; then
30 skip=no
31 continue
32 fi
33 if [ "$skip" = "no" -a "$line" = "# S-T-O-P" ]; then
34 skip=yes
35 continue
36 fi
37 [ "$skip" = "yes" ] && continue
38 # Allow comments.
39 [ -z "${line###*}" ] && continue
40
41 # Parse the line and try to make a decision earliest possible.
42 # Username and endpoint must match values/regexps, action
43 # must exactly match. Action arguments are tested agains values
44 # or regexps, but only for 'change' action.
45 # If the current rule doesn't match, advance to the next one.
46 # We will fail authorization by default anyway.
47
48 # Test action.
49 cval=`echo "$line" | cut -s -d' ' -f3`
50 [ "$action" = "$cval" ] || continue
51
52 # Test username.
53 cval=`echo "$line" | cut -s -d' ' -f2 | cut -s -d'@' -f1`
54 [ -z "${user##$cval}" ] || continue
55
56 # Test endpoint.
57 cval=`echo "$line" | cut -s -d' ' -f2 | cut -s -d'@' -f2`
58 [ -z "${endp##$cval}" ] || continue
59
60 if [ "$action" = "change" ]; then
61 [ -z "$arg1" -o -z "$arg2" ] && return 1
62 cval=`echo "$line" | cut -s -d' ' -f4`
63 [ -z "${arg1##$cval}" ] || continue
64 cval=`echo "$line" | cut -s -d' ' -f5`
65 [ -z "${arg2##$cval}" ] || continue
66 fi
67
68 # All criterias match. Pick the permission and bail out.
69 cval=`echo "$line" | cut -s -d' ' -f1`
70 if [ "$cval" = "allow" ]; then
71 return 0
72 else
73 return 1
74 fi
75 done < `dirname $0`/userauth.php
76 return 1
77 }
78
79 # Not only connect, but gather all the data at once.
80 do_connect()
81 {
82 # sanity checks
83 endpoint=`echo $args | cut -s -d' ' -f1`
84 hw=`echo $args | cut -s -d' ' -f2`
85 sw=`echo $args | cut -s -d' ' -f3`
86 user=`echo $args | cut -s -d' ' -f4`
87 if [ -z "$endpoint" -o -z "$hw" -o -z "$sw" -o -z "$user" ]; then
88 echo 'ERR!too few arguments to connect'
89 return
90 fi
91 # authorize user, look for "connect" privilege
92 if ! authorized $endpoint $user connect; then
93 echo "ERR!User $user is not authorized to connect to $endpoint"
94 return
95 fi
96
97 # prepare temp files
98 PORTINFO=`mktemp /tmp/racktables.XXXX`
99 if ! [ -f "$PORTINFO" ]; then
100 echo 'ERR!could not create portinfo tmpfile'
101 return
102 fi
103 VLANINFO=`mktemp /tmp/racktables.XXXX`
104 if ! [ -f "$VLANINFO" ]; then
105 echo 'ERR!could not create vlaninfo tmpfile'
106 rm -f "$PORTINFO"
107 return
108 fi
109
110 # get the data
111 case "$sw" in
112 Cisco+IOS+12.0|Cisco+IOS+12.2)
113 `dirname $0`/cisco.connector $endpoint fetch "$VLANINFO" "$PORTINFO"
114 ret=$?
115 if [ $ret = 0 ]; then
116 CONNECTED=1
117 echo "OK!connected to $endpoint";
118 else
119 echo "ERR!Cannot connect to $endpoint, connector returned code $ret"
120 fi
121 ;;
122 *)
123 echo "ERR!Don't know how to handle $sw"
124 rm -f "$PORTINFO" "$VLANINFO"
125 ;;
126 esac
127 }
128
129 do_listfile()
130 {
131 local F=$1
132 if ! [ -f "$F" ]; then
133 echo "ERR!Lost temp file '$F' on the way"
134 return
135 fi
136 echo -n 'OK!'
137 local COMMA=''
138 # tr might do the work, but use our chance to perform filtering once more
139 cat "$F" | while read line; do
140 [ "$line" = "" ] && continue
141 echo -n "$COMMA$line"
142 COMMA=','
143 done
144 echo
145 }
146
147 do_set()
148 {
149 # sanity checks
150 local setline=$1
151 if [ -z "$setline" ]; then
152 echo 'ERR!missing set argument'
153 return
154 fi
155 local brand
156 case "$sw" in
157 Cisco+IOS+12.0|Cisco+IOS+12.2)
158 brand=cisco
159 ;;
160 *)
161 echo "ERR!Don't know how to handle $sw at $endpoint"
162 return
163 ;;
164 esac
165 local REQUESTS=`mktemp /tmp/racktables.XXXX`
166 local REPLIES=`mktemp /tmp/racktables.XXXX`
167 echo $1 | tr ';' '\n' | while read setexpr; do
168 portname=`echo $setexpr | cut -s -d'=' -f1`
169 newvlan=`echo $setexpr | cut -s -d'=' -f2`
170 curvlan=`egrep "^$portname=" $PORTINFO | cut -s -d'=' -f2`
171 if [ -z "$curvlan" ]; then
172 echo "E!Could not find port $portname" >> "$REPLIES"
173 continue
174 fi
175 if [ "$curvlan" = "trunk" ]; then
176 echo "E!Port $portname is a trunk" >> "$REPLIES"
177 continue
178 fi
179 [ "$curvlan" = "$newvlan" ] && continue
180 # Authorize user for each change.
181 if ! authorized $endpoint $user change $curvlan $newvlan; then
182 echo "E!User $user is not authorized to assign port $portname@$endpoint from VLAN $curvlan to VLAN $newvlan" >> "$REPLIES"
183 continue
184 fi
185 echo "$portname $newvlan" >> "$REQUESTS"
186 done
187 nr=`egrep -c '^.' "$REPLIES"`
188 if [ "$nr" -ge 1 ]; then
189 echo "W!$nr change request(s) have been ignored" >> "$REPLIES"
190 fi
191
192 nq=`egrep -c '^.' "$REQUESTS"`
193 if [ "$nq" -ge 1 ]; then
194 # Go!
195 `dirname $0`/$brand.connector $endpoint push "$REQUESTS" "$REPLIES"
196 local ret=$?
197
198 if [ $ret != 0 ]; then
199 echo "ERR!Failed to configure $endpoint, connector returned code $ret"
200 return
201 fi
202 echo "I!$nq change request(s) have been processed" >> "$REPLIES"
203 fi
204 echo -n 'OK!'
205 local SEMICOLON=
206 while read reply; do
207 echo -n $SEMICOLON$reply
208 SEMICOLON=';'
209 done < "$REPLIES"
210 echo
211 rm -f "$REQUESTS" "$REPLIES"
212 }
213
214 # main loop
215 while read cmd args; do
216 case $cmd in
217 connect)
218 if [ $CONNECTED = 1 ]; then
219 echo 'ERR!Already connected'
220 else
221 do_connect $args
222 fi
223 ;;
224 listvlans)
225 if [ $CONNECTED = 1 ]; then
226 do_listfile "$VLANINFO"
227 else
228 echo 'ERR!Not connected'
229 fi
230 ;;
231 listports)
232 if [ $CONNECTED = 1 ]; then
233 do_listfile "$PORTINFO"
234 else
235 echo 'ERR!Not connected'
236 fi
237 ;;
238 set)
239 if [ $CONNECTED = 1 ]; then
240 do_set $args
241 else
242 echo 'ERR!Not connected'
243 fi
244 ;;
245 *)
246 echo "ERR!unknown command $cmd"
247 esac
248 done
249
250 rm -f "$PORTINFO" "$VLANINFO"
251 exit 0