r4312 bugfix: undefined index: 'mode' in ios12PickSwitchportCommand
[racktables] / wwwroot / inc / deviceconfig.php
CommitLineData
b519e37e
DO
1<?php
2
3// Read provided output of "show cdp neighbors detail" command and
4// return a list of records with (translated) local port name,
5// remote device name and (translated) remote port name.
6function ios12ReadCDPStatus ($input)
7{
8 $ret = array();
9 foreach (explode ("\n", $input) as $line)
10 {
11 $matches = array();
12 switch (TRUE)
13 {
14 case preg_match ('/^Device ID:\s*([A-Za-z0-9][A-Za-z0-9\.\-]*)/', $line, $matches):
15 case preg_match ('/^System Name:\s*([A-Za-z0-9][A-Za-z0-9\.\-]*)/', $line, $matches):
16 $ret['current']['device'] = $matches[1];
17 break;
18 case preg_match ('/^Interface: (.+), ?Port ID \(outgoing port\): (.+)$/', $line, $matches):
19 if (array_key_exists ('device', $ret['current']))
20 $ret[ios12ShortenIfName ($matches[1])][] = array
21 (
22 'device' => $ret['current']['device'],
23 'port' => ios12ShortenIfName ($matches[2]),
24 );
25 unset ($ret['current']);
26 break;
27 default:
28 }
29 }
30 unset ($ret['current']);
31 return $ret;
32}
33
34function ios12ReadLLDPStatus ($input)
35{
36 $ret = array();
37 $got_header = FALSE;
38 foreach (explode ("\n", $input) as $line)
39 {
40 if (preg_match ("/^Device ID/", $line))
41 $got_header = TRUE;
42
43 if (!$got_header)
44 continue;
45
46 $matches = preg_split ('/\s+/', $line);
47
48 switch (count ($matches))
49 {
50 case 5:
51 list ($remote_name, $local_port, $ttl, $caps, $remote_port) = $matches;
52 $local_port = ios12ShortenIfName ($local_port);
53 $remote_port = ios12ShortenIfName ($remote_port);
54 $ret[$local_port][] = array
55 (
56 'device' => $remote_name,
57 'port' => $remote_port,
58 );
59 break;
60 default:
61 }
62 }
63 return $ret;
64}
65
66function xos12ReadLLDPStatus ($input)
67{
68 $ret = array();
69 foreach (explode ("\n", $input) as $line)
70 {
71 $matches = array();
72 switch (TRUE)
73 {
74 case preg_match ('/^LLDP Port ([[:digit:]]+) detected \d+ neighbor$/', $line, $matches):
75 $ret['current']['local_port'] = ios12ShortenIfName ($matches[1]);
76 break;
77 case preg_match ('/^ Port ID : "(.+)"$/', $line, $matches):
78 $ret['current']['remote_port'] = ios12ShortenIfName ($matches[1]);
79 break;
80 case preg_match ('/^ - System Name: "(.+)"$/', $line, $matches):
81 if
82 (
83 array_key_exists ('current', $ret) and
84 array_key_exists ('local_port', $ret['current']) and
85 array_key_exists ('remote_port', $ret['current'])
86 )
87 $ret[$ret['current']['local_port']][] = array
88 (
89 'device' => $matches[1],
90 'port' => $ret['current']['remote_port'],
91 );
92 unset ($ret['current']);
93 default:
94 }
95 }
96 unset ($ret['current']);
97 return $ret;
98}
99
100function vrp53ReadLLDPStatus ($input)
101{
102 $ret = array();
103 foreach (explode ("\n", $input) as $line)
104 {
105 $matches = array();
106 switch (TRUE)
107 {
108 case preg_match ('/^(.+) has \d+ neighbors:$/', $line, $matches):
109 $ret['current']['local_port'] = ios12ShortenIfName ($matches[1]);
110 break;
111 case preg_match ('/^(PortIdSubtype|PortId): ([^ ]+)/', $line, $matches):
112 $ret['current'][$matches[1]] = $matches[2];
113 break;
114 case preg_match ('/^SysName: (.+)$/', $line, $matches):
115 if
116 (
117 array_key_exists ('current', $ret) and
118 array_key_exists ('PortIdSubtype', $ret['current']) and
119 ($ret['current']['PortIdSubtype'] == 'interfaceAlias' or $ret['current']['PortIdSubtype'] == 'interfaceName') and
120 array_key_exists ('PortId', $ret['current']) and
121 array_key_exists ('local_port', $ret['current'])
122 )
123 $ret[$ret['current']['local_port']][] = array
124 (
125 'device' => $matches[1],
126 'port' => ios12ShortenIfName ($ret['current']['PortId']),
127 );
128 unset ($ret['current']);
129 break;
130 default:
131 }
132 }
133 unset ($ret['current']);
134 return $ret;
135}
136
137function vrp55ReadLLDPStatus ($input)
138{
139 $ret = array();
140 foreach (explode ("\n", $input) as $line)
141 {
142 $matches = array();
143 switch (TRUE)
144 {
145 case preg_match ('/^(.+) has \d+ neighbors:$/', $line, $matches):
146 $ret['current']['local_port'] = ios12ShortenIfName ($matches[1]);
147 break;
148 case preg_match ('/^Port ID type :([^ ]+)/', $line, $matches):
149 $ret['current']['PortIdSubtype'] = $matches[1];
150 break;
151 case preg_match ('/^Port ID :(.+)$/', $line, $matches):
152 $ret['current']['PortId'] = $matches[1];
153 break;
154 case preg_match ('/^System name :(.+)$/', $line, $matches):
155 if
156 (
157 array_key_exists ('current', $ret) and
158 array_key_exists ('PortIdSubtype', $ret['current']) and
159 ($ret['current']['PortIdSubtype'] == 'interfaceAlias' or $ret['current']['PortIdSubtype'] == 'interfaceName') and
160 array_key_exists ('PortId', $ret['current']) and
161 array_key_exists ('local_port', $ret['current'])
162 )
163 $ret[$ret['current']['local_port']][] = array
164 (
165 'device' => $matches[1],
166 'port' => ios12ShortenIfName ($ret['current']['PortId']),
167 );
168 unset ($ret['current']);
169 break;
170 default:
171 }
172 }
173 unset ($ret['current']);
174 return $ret;
175}
176
177function vrp53ReadHNDPStatus ($input)
178{
179 $ret = array();
180 foreach (explode ("\n", $input) as $line)
181 {
182 $matches = array();
183 switch (TRUE)
184 {
185 case preg_match ('/^ Interface: (.+)$/', $line, $matches):
186 $ret['current']['local_port'] = ios12ShortenIfName ($matches[1]);
187 break;
188 case preg_match ('/^ Port Name : (.+)$/', $line, $matches):
189 $ret['current']['remote_port'] = ios12ShortenIfName ($matches[1]);
190 break;
191 case preg_match ('/^ Device Name : (.+)$/', $line, $matches):
192 if
193 (
194 array_key_exists ('current', $ret) and
195 array_key_exists ('local_port', $ret['current']) and
196 array_key_exists ('remote_port', $ret['current'])
197 )
198 $ret[$ret['current']['local_port']][] = array
199 (
200 'device' => $matches[1],
201 'port' => $ret['current']['remote_port'],
202 );
203 unset ($ret['current']);
204 break;
205 default:
206 }
207 }
208 unset ($ret['current']);
209 return $ret;
210}
211
212function ios12ReadVLANConfig ($input)
213{
214 $ret = array
215 (
216 'vlanlist' => array(),
217 'portdata' => array(),
218 );
219 $procfunc = 'ios12ScanTopLevel';
220 foreach (explode ("\n", $input) as $line)
221 $procfunc = $procfunc ($ret, $line);
222 return $ret;
223}
224
225function ios12ScanTopLevel (&$work, $line)
226{
227 $matches = array();
228 switch (TRUE)
229 {
230 case (preg_match ('@^interface ((Ethernet|FastEthernet|GigabitEthernet|TenGigabitEthernet|Port-channel)[[:digit:]]+(/[[:digit:]]+)*)$@', $line, $matches)):
231 $work['current'] = array ('port_name' => ios12ShortenIfName ($matches[1]));
232 $work['current']['config'][] = array ('type' => 'line-header', 'line' => $line);
233 return 'ios12PickSwitchportCommand'; // switch to interface block reading
234 case (preg_match ('/^VLAN Name Status Ports$/', $line, $matches)):
235 return 'ios12PickVLANCommand';
236 default:
237 return __FUNCTION__; // continue scan
238 }
239}
240
241function ios12PickSwitchportCommand (&$work, $line)
242{
243 if ($line[0] != ' ') // end of interface section
244 {
245 // save work, if it makes sense
999eaf31 246 switch (@$work['current']['mode'])
b519e37e
DO
247 {
248 case 'access':
249 if (!array_key_exists ('access vlan', $work['current']))
250 $work['current']['access vlan'] = 1;
251 $work['portdata'][$work['current']['port_name']] = array
252 (
253 'mode' => 'access',
254 'allowed' => array ($work['current']['access vlan']),
255 'native' => $work['current']['access vlan'],
256 );
257 break;
258 case 'trunk':
259 if (!array_key_exists ('trunk native vlan', $work['current']))
260 $work['current']['trunk native vlan'] = 1;
261 if (!array_key_exists ('trunk allowed vlan', $work['current']))
262 $work['current']['trunk allowed vlan'] = range (VLAN_MIN_ID, VLAN_MAX_ID);
263 // Having configured VLAN as "native" doesn't mean anything
264 // as long as it's not listed on the "allowed" line.
265 $effective_native = in_array
266 (
267 $work['current']['trunk native vlan'],
268 $work['current']['trunk allowed vlan']
269 ) ? $work['current']['trunk native vlan'] : 0;
270 $work['portdata'][$work['current']['port_name']] = array
271 (
272 'mode' => 'trunk',
273 'allowed' => $work['current']['trunk allowed vlan'],
274 'native' => $effective_native,
275 );
276 break;
277 case 'SKIP':
278 break;
279 case 'IP':
280 default:
281 // dot1q-tunnel, dynamic, private-vlan or even none --
282 // show in returned config and let user decide, if they
283 // want to fix device config or work around these ports
284 // by means of VST.
285 $work['portdata'][$work['current']['port_name']] = array
286 (
287 'mode' => 'none',
288 'allowed' => array(),
289 'native' => 0,
290 );
291 break;
292 }
293 if (isset ($work['portdata'][$work['current']['port_name']]))
294 $work['portdata'][$work['current']['port_name']]['config'] = $work['current']['config'];
295 unset ($work['current']);
296 return 'ios12ScanTopLevel';
297 }
298 // not yet
299 $matches = array();
300 $line_class = 'line-8021q';
301 switch (TRUE)
302 {
303 case (preg_match ('@^ switchport mode (.+)$@', $line, $matches)):
304 $work['current']['mode'] = $matches[1];
305 break;
306 case (preg_match ('@^ switchport access vlan (.+)$@', $line, $matches)):
307 $work['current']['access vlan'] = $matches[1];
308 break;
309 case (preg_match ('@^ switchport trunk native vlan (.+)$@', $line, $matches)):
310 $work['current']['trunk native vlan'] = $matches[1];
311 break;
312 case (preg_match ('@^ switchport trunk allowed vlan add (.+)$@', $line, $matches)):
313 $work['current']['trunk allowed vlan'] = array_merge
314 (
315 $work['current']['trunk allowed vlan'],
316 iosParseVLANString ($matches[1])
317 );
318 break;
319 case (preg_match ('@^ switchport trunk allowed vlan (.+)$@', $line, $matches)):
320 $work['current']['trunk allowed vlan'] = iosParseVLANString ($matches[1]);
321 break;
322 case preg_match ('@^ channel-group @', $line):
323 // port-channel subinterface config follows that of the master interface
324 $work['current']['mode'] = 'SKIP';
325 break;
326 case preg_match ('@^ ip address @', $line):
327 // L3 interface does no switchport functions
328 $work['current']['mode'] = 'IP';
329 break;
330 default: // suppress warning on irrelevant config clause
331 $line_class = 'line-other';
332 }
333 $work['current']['config'][] = array ('type' => $line_class, 'line' => $line);
334 return __FUNCTION__;
335}
336
337function ios12PickVLANCommand (&$work, $line)
338{
339 $matches = array();
340 switch (TRUE)
341 {
342 case ($line == '---- -------------------------------- --------- -------------------------------'):
343 // ignore the rest of VLAN table header;
344 break;
345 case (preg_match ('@! END OF VLAN LIST$@', $line)):
346 return 'ios12ScanTopLevel';
347 case (preg_match ('@^([[:digit:]]+) {1,4}.{32} active @', $line, $matches)):
348 if (!array_key_exists ($matches[1], $work['vlanlist']))
349 $work['vlanlist'][] = $matches[1];
350 break;
351 default:
352 }
353 return __FUNCTION__;
354}
355
356// Another finite automata to read a dialect of Foundry configuration.
357function fdry5ReadVLANConfig ($input)
358{
359 $ret = array
360 (
361 'vlanlist' => array(),
362 'portdata' => array(),
363 );
364 $procfunc = 'fdry5ScanTopLevel';
365 foreach (explode ("\n", $input) as $line)
366 $procfunc = $procfunc ($ret, $line);
367 return $ret;
368}
369
370function fdry5ScanTopLevel (&$work, $line)
371{
372 $matches = array();
373 switch (TRUE)
374 {
375 case (preg_match ('@^vlan ([[:digit:]]+)( name .+)? (by port)$@', $line, $matches)):
376 if (!array_key_exists ($matches[1], $work['vlanlist']))
377 $work['vlanlist'][] = $matches[1];
378 $work['current'] = array ('vlan_id' => $matches[1]);
379 return 'fdry5PickVLANSubcommand';
380 case (preg_match ('@^interface ethernet ([[:digit:]]+/[[:digit:]]+/[[:digit:]]+)$@', $line, $matches)):
381 $work['current'] = array ('port_name' => 'e' . $matches[1]);
382 return 'fdry5PickInterfaceSubcommand';
383 default:
384 return __FUNCTION__;
385 }
386}
387
388function fdry5PickVLANSubcommand (&$work, $line)
389{
390 if ($line[0] != ' ') // end of VLAN section
391 {
392 unset ($work['current']);
393 return 'fdry5ScanTopLevel';
394 }
395 // not yet
396 $matches = array();
397 switch (TRUE)
398 {
399 case (preg_match ('@^ tagged (.+)$@', $line, $matches)):
400 // add current VLAN to 'allowed' list of each mentioned port
401 foreach (fdry5ParsePortString ($matches[1]) as $port_name)
402 if (array_key_exists ($port_name, $work['portdata']))
403 $work['portdata'][$port_name]['allowed'][] = $work['current']['vlan_id'];
404 else
405 $work['portdata'][$port_name] = array
406 (
407 'mode' => 'trunk',
408 'allowed' => array ($work['current']['vlan_id']),
409 'native' => 0, // can be updated later
410 );
411 $work['portdata'][$port_name]['mode'] = 'trunk';
412 break;
413 case (preg_match ('@^ untagged (.+)$@', $line, $matches)):
414 // replace 'native' column of each mentioned port with current VLAN ID
415 foreach (fdry5ParsePortString ($matches[1]) as $port_name)
416 {
417 if (array_key_exists ($port_name, $work['portdata']))
418 {
419 $work['portdata'][$port_name]['native'] = $work['current']['vlan_id'];
420 $work['portdata'][$port_name]['allowed'][] = $work['current']['vlan_id'];
421 }
422 else
423 $work['portdata'][$port_name] = array
424 (
425 'mode' => 'access',
426 'allowed' => array ($work['current']['vlan_id']),
427 'native' => $work['current']['vlan_id'],
428 );
429 // Untagged ports are initially assumed to be access ports, and
430 // when this assumption is right, this is the final port mode state.
431 // When the port is dual-mode one, this is detected and justified
432 // later in "interface" section of config text.
433 $work['portdata'][$port_name]['mode'] = 'access';
434 }
435 break;
436 default: // nom-nom
437 }
438 return __FUNCTION__;
439}
440
441function fdry5PickInterfaceSubcommand (&$work, $line)
442{
443 if ($line[0] != ' ') // end of interface section
444 {
445 if (array_key_exists ('dual-mode', $work['current']))
446 {
447 if (array_key_exists ($work['current']['port_name'], $work['portdata']))
448 // update existing record
449 $work['portdata'][$work['current']['port_name']]['native'] = $work['current']['dual-mode'];
450 else
451 // add new
452 $work['portdata'][$work['current']['port_name']] = array
453 (
454 'allowed' => array ($work['current']['dual-mode']),
455 'native' => $work['current']['dual-mode'],
456 );
457 // a dual-mode port is always considered a trunk port
458 // (but not in the IronWare's meaning of "trunk") regardless of
459 // number of assigned tagged VLANs
460 $work['portdata'][$work['current']['port_name']]['mode'] = 'trunk';
461 }
462 unset ($work['current']);
463 return 'fdry5ScanTopLevel';
464 }
465 $matches = array();
466 switch (TRUE)
467 {
468 case (preg_match ('@^ dual-mode( +[[:digit:]]+ *)?$@', $line, $matches)):
469 // default VLAN ID for dual-mode command is 1
470 $work['current']['dual-mode'] = strlen (trim ($matches[1])) ? trim ($matches[1]) : 1;
471 break;
472 // FIXME: trunk/link-aggregate/ip address pulls port from 802.1Q field
473 default: // nom-nom
474 }
475 return __FUNCTION__;
476}
477
478function fdry5ParsePortString ($string)
479{
480 $ret = array();
481 $tokens = explode (' ', trim ($string));
482 while (count ($tokens))
483 {
484 $letters = array_shift ($tokens); // "ethe", "to"
485 $numbers = array_shift ($tokens); // "x", "x/x", "x/x/x"
486 switch ($letters)
487 {
488 case 'ethe':
489 if ($prev_numbers != NULL)
490 $ret[] = 'e' . $prev_numbers;
491 $prev_numbers = $numbers;
492 break;
493 case 'to':
494 $ret = array_merge ($ret, fdry5GenPortRange ($prev_numbers, $numbers));
495 $prev_numbers = NULL; // no action on next token
496 break;
497 default: // ???
498 return array();
499 }
500 }
501 // flush delayed item
502 if ($prev_numbers != NULL)
503 $ret[] = 'e' . $prev_numbers;
504 return $ret;
505}
506
507// Take two indices in form "x", "x/x" or "x/x/x" and return the range of
508// ports spanning from the first to the last. The switch software makes it
509// easier to perform, because "ethe x/x/x to y/y/y" ranges never cross
510// unit/slot boundary (every index except the last remains constant).
511function fdry5GenPortRange ($from, $to)
512{
513 $matches = array();
514 if (1 !== preg_match ('@^([[:digit:]]+/)?([[:digit:]]+/)?([[:digit:]]+)$@', $from, $matches))
515 return array();
516 $prefix = 'e' . $matches[1] . $matches[2];
517 $from_idx = $matches[3];
518 if (1 !== preg_match ('@^([[:digit:]]+/)?([[:digit:]]+/)?([[:digit:]]+)$@', $to, $matches))
519 return array();
520 $to_idx = $matches[3];
521 for ($i = $from_idx; $i <= $to_idx; $i++)
522 $ret[] = $prefix . $i;
523 return $ret;
524}
525
526// an implementation for Huawei syntax
527function vrp53ReadVLANConfig ($input)
528{
529 $ret = array
530 (
531 'vlanlist' => array(),
532 'portdata' => array(),
533 );
534 $procfunc = 'vrp53ScanTopLevel';
535 foreach (explode ("\n", $input) as $line)
536 $procfunc = $procfunc ($ret, $line);
537 return $ret;
538}
539
540function vrp53ScanTopLevel (&$work, $line)
541{
542 $matches = array();
543 switch (TRUE)
544 {
545 case (preg_match ('@^ vlan batch (.+)$@', $line, $matches)):
546 foreach (vrp53ParseVLANString ($matches[1]) as $vlan_id)
547 $work['vlanlist'][] = $vlan_id;
548 return __FUNCTION__;
549 case (preg_match ('@^interface ((GigabitEthernet|XGigabitEthernet|Eth-Trunk)([[:digit:]]+(/[[:digit:]]+)*))$@', $line, $matches)):
550 $matches[1] = preg_replace ('@^GigabitEthernet(.+)$@', 'gi\\1', $matches[1]);
551 $matches[1] = preg_replace ('@^XGigabitEthernet(.+)$@', 'xg\\1', $matches[1]);
552 $matches[1] = preg_replace ('@^Eth-Trunk(.+)$@', 'et\\1', $matches[1]);
553 $work['current'] = array ('port_name' => $matches[1]);
554 $work['current']['config'][] = array ('type' => 'line-header', 'line' => $line);
555 return 'vrp53PickInterfaceSubcommand';
556 default:
557 return __FUNCTION__;
558 }
559}
560
561function vrp53ParseVLANString ($string)
562{
563 $string = preg_replace ('/ to /', '-', $string);
564 $string = preg_replace ('/ /', ',', $string);
565 return iosParseVLANString ($string);
566}
567
568function vrp53PickInterfaceSubcommand (&$work, $line)
569{
570 if ($line[0] == '#') // end of interface section
571 {
572 // Configuration Guide - Ethernet 3.3.4:
573 // "By default, the interface type is hybrid."
574 if (!array_key_exists ('link-type', $work['current']))
575 $work['current']['link-type'] = 'hybrid';
576 if (!array_key_exists ('allowed', $work['current']))
577 $work['current']['allowed'] = array();
578 if (!array_key_exists ('native', $work['current']))
579 $work['current']['native'] = 0;
580 switch ($work['current']['link-type'])
581 {
582 case 'access':
583 // VRP does not assign access ports to VLAN1 by default,
584 // leaving them blocked.
585 $work['portdata'][$work['current']['port_name']] =
586 $work['current']['native'] ? array
587 (
588 'allowed' => $work['current']['allowed'],
589 'native' => $work['current']['native'],
590 'mode' => 'access',
591 ) : array
592 (
593 'mode' => 'none',
594 'allowed' => array(),
595 'native' => 0,
596 );
597 break;
598 case 'trunk':
599 $work['portdata'][$work['current']['port_name']] = array
600 (
601 'allowed' => $work['current']['allowed'],
602 'native' => 0,
603 'mode' => 'trunk',
604 );
605 break;
606 case 'hybrid':
607 $work['portdata'][$work['current']['port_name']] = array
608 (
609 'allowed' => $work['current']['allowed'],
610 'native' => $work['current']['native'],
611 'mode' => 'trunk',
612 );
613 break;
614 default: // dot1q-tunnel ?
615 }
616 if (isset ($work['portdata'][$work['current']['port_name']]))
617 $work['portdata'][$work['current']['port_name']]['config'] = $work['current']['config'];
618 unset ($work['current']);
619 return 'vrp53ScanTopLevel';
620 }
621 $matches = array();
622 $line_class = 'line-8021q';
623 switch (TRUE)
624 {
625 case (preg_match ('@^ port default vlan ([[:digit:]]+)$@', $line, $matches)):
626 $work['current']['native'] = $matches[1];
627 if (!array_key_exists ('allowed', $work['current']))
628 $work['current']['allowed'] = array();
629 if (!in_array ($matches[1], $work['current']['allowed']))
630 $work['current']['allowed'][] = $matches[1];
631 break;
632 case (preg_match ('@^ port link-type (.+)$@', $line, $matches)):
633 $work['current']['link-type'] = $matches[1];
634 break;
635 case (preg_match ('@^ port trunk allow-pass vlan (.+)$@', $line, $matches)):
636 if (!array_key_exists ('allowed', $work['current']))
637 $work['current']['allowed'] = array();
638 foreach (vrp53ParseVLANString ($matches[1]) as $vlan_id)
639 if (!in_array ($vlan_id, $work['current']['allowed']))
640 $work['current']['allowed'][] = $vlan_id;
641 break;
642 // TODO: make sure, that a port with "eth-trunk" clause always ends up in "none" mode
643 default: // nom-nom
644 $line_class = 'line-other';
645 }
646 $work['current']['config'][] = array('type' => $line_class, 'line' => $line);
647 return __FUNCTION__;
648}
649
650function vrp55Read8021QConfig ($input)
651{
652 $ret = array
653 (
654 'vlanlist' => array (1), // VRP 5.50 hides VLAN1 from config text
655 'portdata' => array(),
656 );
657 foreach (explode ("\n", $input) as $line)
658 {
659 $matches = array();
660 // top level
661 if (!array_key_exists ('current', $ret))
662 {
663 switch (TRUE)
664 {
665 case (preg_match ('@^ vlan batch (.+)$@', $line, $matches)):
666 foreach (vrp53ParseVLANString ($matches[1]) as $vlan_id)
667 $ret['vlanlist'][] = $vlan_id;
668 break;
669 case (preg_match ('@^interface ((GigabitEthernet|XGigabitEthernet|Eth-Trunk)([[:digit:]]+(/[[:digit:]]+)*))$@', $line, $matches)):
670 $matches[1] = preg_replace ('@^GigabitEthernet(.+)$@', 'gi\\1', $matches[1]);
671 $matches[1] = preg_replace ('@^XGigabitEthernet(.+)$@', 'xg\\1', $matches[1]);
672 $ret['current'] = array ('port_name' => $matches[1]);
673 $ret['current']['config'][] = array ('type' => 'line-header', 'line' => $line);
674 break;
675 }
676 continue;
677 }
678 // inside an interface block
679 $line_class = 'line-8021q';
680 switch (TRUE)
681 {
682 case preg_match ('/^ port (link-type )?hybrid /', $line):
683 throw new RTGatewayError ("unsupported configuration: ${line}");
684 case preg_match ('/^ port link-type (.+)$/', $line, $matches):
685 $ret['current']['link-type'] = $matches[1];
686 break;
687 // Native VLAN is configured differently for each link-type case, but
688 // VRP is known to filter off clauses, which don't make sense for
689 // current link-type. This way any interface section should contain
690 // only one kind of "set native" clause (but if this constraint breaks,
691 // we get a problem).
692 case preg_match ('/^ port (default|trunk pvid) vlan ([[:digit:]]+)$/', $line, $matches):
693 $ret['current']['native'] = $matches[2];
694 if (!array_key_exists ('allowed', $ret['current']))
695 $ret['current']['allowed'] = array();
696 if (!in_array ($ret['current']['native'], $ret['current']['allowed']))
697 $ret['current']['allowed'][] = $ret['current']['native'];
698 break;
699 case preg_match ('/^ port trunk allow-pass vlan (.+)$/', $line, $matches):
700 if (!array_key_exists ('allowed', $ret['current']))
701 $ret['current']['allowed'] = array();
702 foreach (vrp53ParseVLANString ($matches[1]) as $vlan_id)
703 if (!in_array ($vlan_id, $ret['current']['allowed']))
704 $ret['current']['allowed'][] = $vlan_id;
705 break;
706 case $line == ' undo portswitch':
707 case preg_match ('/^ ip address /', $line):
708 $ret['current']['link-type'] = 'IP';
709 break;
710 case preg_match ('/^ eth-trunk /', $line):
711 $ret['current']['link-type'] = 'SKIP';
712 break;
713 case substr ($line, 0, 1) == '#': // end of interface section
714 if (!array_key_exists ('link-type', $ret['current']))
715 throw new RTGatewayError ('unsupported configuration: link-type is neither trunk nor access for ' . $ret['current']['port_name']);
716 if (!array_key_exists ('allowed', $ret['current']))
717 $ret['current']['allowed'] = array();
718 if (!array_key_exists ('native', $ret['current']))
719 $ret['current']['native'] = 0;
720 switch ($ret['current']['link-type'])
721 {
722 case 'access':
723 // In VRP 5.50 an access port has default VLAN ID == 1
724 $ret['portdata'][$ret['current']['port_name']] =
725 $ret['current']['native'] ? array
726 (
727 'mode' => 'access',
728 'allowed' => $ret['current']['allowed'],
729 'native' => $ret['current']['native'],
730 ) : array
731 (
732 'mode' => 'access',
733 'allowed' => array (VLAN_DFL_ID),
734 'native' => VLAN_DFL_ID,
735 );
736 break;
737 case 'trunk':
738 $ret['portdata'][$ret['current']['port_name']] = array
739 (
740 'mode' => 'trunk',
741 'allowed' => $ret['current']['allowed'],
742 'native' => $ret['current']['native'],
743 );
744 break;
745 case 'IP':
746 $ret['portdata'][$ret['current']['port_name']] = array
747 (
748 'mode' => 'none',
749 'allowed' => array(),
750 'native' => 0,
751 );
752 break;
753 case 'SKIP':
754 default: // dot1q-tunnel ?
755 }
756 if (isset ($ret['portdata'][$ret['current']['port_name']]))
757 $ret['portdata'][$ret['current']['port_name']]['config'] = $ret['current']['config'];
758 unset ($ret['current']);
759 continue 2;
760 default: // nom-nom
761 $line_class = 'line-other';
762 }
763 $ret['current']['config'][] = array ('type' => $line_class, 'line' => $line);
764 }
765 return $ret;
766}
767
768function nxos4Read8021QConfig ($input)
769{
770 $ret = array
771 (
772 'vlanlist' => array(),
773 'portdata' => array(),
774 );
775 $procfunc = 'nxos4ScanTopLevel';
776 foreach (explode ("\n", $input) as $line)
777 $procfunc = $procfunc ($ret, $line);
778 return $ret;
779}
780
781function nxos4ScanTopLevel (&$work, $line)
782{
783 $matches = array();
784 switch (TRUE)
785 {
786 case (preg_match ('@^interface ((Ethernet|Port-channel)[[:digit:]]+(/[[:digit:]]+)*)$@i', $line, $matches)):
787 $matches[1] = preg_replace ('@^Ethernet(.+)$@i', 'e\\1', $matches[1]);
788 $matches[1] = preg_replace ('@^Port-channel(.+)$@i', 'po\\1', $matches[1]);
789 $work['current'] = array ('port_name' => $matches[1]);
790 $work['current']['config'][] = array ('type' => 'line-header', 'line' => $line);
791 return 'nxos4PickSwitchportCommand';
792 case (preg_match ('@^vlan (.+)$@', $line, $matches)):
793 foreach (iosParseVLANString ($matches[1]) as $vlan_id)
794 $work['vlanlist'][] = $vlan_id;
795 return __FUNCTION__;
796 default:
797 return __FUNCTION__; // continue scan
798 }
799}
800
801function nxos4PickSwitchportCommand (&$work, $line)
802{
803 if ($line == '') // end of interface section
804 {
805 // fill in defaults
806 if (!array_key_exists ('mode', $work['current']))
807 $work['current']['mode'] = 'access';
808 // save work, if it makes sense
809 switch ($work['current']['mode'])
810 {
811 case 'access':
812 if (!array_key_exists ('access vlan', $work['current']))
813 $work['current']['access vlan'] = 1;
814 $work['portdata'][$work['current']['port_name']] = array
815 (
816 'mode' => 'access',
817 'allowed' => array ($work['current']['access vlan']),
818 'native' => $work['current']['access vlan'],
819 );
820 break;
821 case 'trunk':
822 if (!array_key_exists ('trunk native vlan', $work['current']))
823 $work['current']['trunk native vlan'] = 1;
824 // FIXME: NX-OS reserves VLANs 3968 through 4047 plus 4094 for itself
825 if (!array_key_exists ('trunk allowed vlan', $work['current']))
826 $work['current']['trunk allowed vlan'] = range (VLAN_MIN_ID, VLAN_MAX_ID);
827 // Having configured VLAN as "native" doesn't mean anything
828 // as long as it's not listed on the "allowed" line.
829 $effective_native = in_array
830 (
831 $work['current']['trunk native vlan'],
832 $work['current']['trunk allowed vlan']
833 ) ? $work['current']['trunk native vlan'] : 0;
834 $work['portdata'][$work['current']['port_name']] = array
835 (
836 'mode' => 'trunk',
837 'allowed' => $work['current']['trunk allowed vlan'],
838 'native' => $effective_native,
839 );
840 break;
841 case 'SKIP':
842 case 'fex-fabric': // associated port-channel
843 break;
844 default:
845 // dot1q-tunnel, dynamic, private-vlan
846 $work['portdata'][$work['current']['port_name']] = array
847 (
848 'mode' => 'none',
849 'allowed' => array(),
850 'native' => 0,
851 );
852 // unset (routed), dot1q-tunnel, dynamic, private-vlan --- skip these
853 }
854 if (isset ($work['portdata'][$work['current']['port_name']]))
855 $work['portdata'][$work['current']['port_name']]['config'] = $work['current']['config'];
856 unset ($work['current']);
857 return 'nxos4ScanTopLevel';
858 }
859 // not yet
860 $matches = array();
861 $line_class = 'line-8021q';
862 switch (TRUE)
863 {
864 case (preg_match ('@^ switchport mode (.+)$@', $line, $matches)):
865 $work['current']['mode'] = $matches[1];
866 break;
867 case (preg_match ('@^ switchport access vlan (.+)$@', $line, $matches)):
868 $work['current']['access vlan'] = $matches[1];
869 break;
870 case (preg_match ('@^ switchport trunk native vlan (.+)$@', $line, $matches)):
871 $work['current']['trunk native vlan'] = $matches[1];
872 break;
873 case (preg_match ('@^ switchport trunk allowed vlan add (.+)$@', $line, $matches)):
874 $work['current']['trunk allowed vlan'] = array_merge
875 (
876 $work['current']['trunk allowed vlan'],
877 iosParseVLANString ($matches[1])
878 );
879 break;
880 case (preg_match ('@^ switchport trunk allowed vlan (.+)$@', $line, $matches)):
881 $work['current']['trunk allowed vlan'] = iosParseVLANString ($matches[1]);
882 break;
883 case preg_match ('/^ +channel-group /', $line):
884 $work['current']['mode'] = 'SKIP';
885 break;
886 default: // suppress warning on irrelevant config clause
887 $line_class = 'line-other';
888 }
889 $work['current']['config'][] = array ('type' => $line_class, 'line' => $line);
890 return __FUNCTION__;
891}
892
893// Get a list of VLAN management pseudo-commands and return a text
894// of real vendor-specific commands, which implement the work.
895// This work is done in two rounds:
896// 1. For "add allowed" and "rem allowed" commands detect continuous
897// sequences of VLAN IDs and replace them with ranges of form "A-B",
898// where B>A.
899// 2. Iterate over the resulting list and produce real CLI commands.
900function ios12TranslatePushQueue ($queue)
901{
902 $ret = '';
903 foreach ($queue as $cmd)
904 switch ($cmd['opcode'])
905 {
906 case 'create VLAN':
907 $ret .= "vlan ${cmd['arg1']}\nexit\n";
908 break;
909 case 'destroy VLAN':
910 $ret .= "no vlan ${cmd['arg1']}\n";
911 break;
912 case 'add allowed':
913 case 'rem allowed':
914 $clause = $cmd['opcode'] == 'add allowed' ? 'add' : 'remove';
915 $ret .= "interface ${cmd['port']}\n";
916 foreach (listToRanges ($cmd['vlans']) as $range)
917 $ret .= "switchport trunk allowed vlan ${clause} " .
918 ($range['from'] == $range['to'] ? $range['to'] : "${range['from']}-${range['to']}") .
919 "\n";
920 $ret .= "exit\n";
921 break;
922 case 'set native':
923 $ret .= "interface ${cmd['arg1']}\nswitchport trunk native vlan ${cmd['arg2']}\nexit\n";
924 break;
925 case 'unset native':
926 $ret .= "interface ${cmd['arg1']}\nno switchport trunk native vlan ${cmd['arg2']}\nexit\n";
927 break;
928 case 'set access':
929 $ret .= "interface ${cmd['arg1']}\nswitchport access vlan ${cmd['arg2']}\nexit\n";
930 break;
931 case 'unset access':
932 $ret .= "interface ${cmd['arg1']}\nno switchport access vlan\nexit\n";
933 break;
934 case 'set mode':
935 $ret .= "interface ${cmd['arg1']}\nswitchport mode ${cmd['arg2']}\n";
936 if ($cmd['arg2'] == 'trunk')
937 $ret .= "no switchport trunk native vlan\nswitchport trunk allowed vlan none\n";
938 $ret .= "exit\n";
939 break;
940 case 'begin configuration':
941 $ret .= "configure terminal\n";
942 break;
943 case 'end configuration':
944 $ret .= "end\n";
945 break;
946 case 'save configuration':
947 $ret .= "copy running-config startup-config\n\n";
948 break;
949 default:
950 throw new InvalidArgException ('opcode', $cmd['opcode']);
951 }
952 return $ret;
953}
954
955function fdry5TranslatePushQueue ($queue)
956{
957 $ret = '';
958 foreach ($queue as $cmd)
959 switch ($cmd['opcode'])
960 {
961 case 'create VLAN':
962 $ret .= "vlan ${cmd['arg1']}\nexit\n";
963 break;
964 case 'destroy VLAN':
965 $ret .= "no vlan ${cmd['arg1']}\n";
966 break;
967 case 'add allowed':
968 foreach ($cmd['vlans'] as $vlan_id)
969 $ret .= "vlan ${vlan_id}\ntagged ${cmd['port']}\nexit\n";
970 break;
971 case 'rem allowed':
972 foreach ($cmd['vlans'] as $vlan_id)
973 $ret .= "vlan ${vlan_id}\nno tagged ${cmd['port']}\nexit\n";
974 break;
975 case 'set native':
976 $ret .= "interface ${cmd['arg1']}\ndual-mode ${cmd['arg2']}\nexit\n";
977 break;
978 case 'unset native':
979 $ret .= "interface ${cmd['arg1']}\nno dual-mode ${cmd['arg2']}\nexit\n";
980 break;
981 case 'set access':
982 $ret .= "vlan ${cmd['arg2']}\nuntagged ${cmd['arg1']}\nexit\n";
983 break;
984 case 'unset access':
985 $ret .= "vlan ${cmd['arg2']}\nno untagged ${cmd['arg1']}\nexit\n";
986 break;
987 case 'set mode': // NOP
988 break;
989 case 'begin configuration':
990 $ret .= "conf t\n";
991 break;
992 case 'end configuration':
993 $ret .= "end\n";
994 break;
995 case 'save configuration':
996 $ret .= "write memory\n";
997 break;
998 default:
999 throw new InvalidArgException ('opcode', $cmd['opcode']);
1000 }
1001 return $ret;
1002}
1003
1004function vrp53TranslatePushQueue ($queue)
1005{
1006 $ret = '';
1007 foreach ($queue as $cmd)
1008 switch ($cmd['opcode'])
1009 {
1010 case 'create VLAN':
1011 $ret .= "vlan ${cmd['arg1']}\nquit\n";
1012 break;
1013 case 'destroy VLAN':
1014 $ret .= "undo vlan ${cmd['arg1']}\n";
1015 break;
1016 case 'add allowed':
1017 case 'rem allowed':
1018 $clause = $cmd['opcode'] == 'add allowed' ? '' : 'undo ';
1019 $ret .= "interface ${cmd['port']}\n";
1020 foreach (listToRanges ($cmd['vlans']) as $range)
1021 $ret .= "${clause}port trunk allow-pass vlan " .
1022 ($range['from'] == $range['to'] ? $range['to'] : "${range['from']} to ${range['to']}") .
1023 "\n";
1024 $ret .= "quit\n";
1025 break;
1026 case 'set native':
1027 case 'set access':
1028 $ret .= "interface ${cmd['arg1']}\nport default vlan ${cmd['arg2']}\nquit\n";
1029 break;
1030 case 'unset native':
1031 case 'unset access':
1032 $ret .= "interface ${cmd['arg1']}\nundo port default vlan\nquit\n";
1033 break;
1034 case 'set mode':
1035 $modemap = array ('access' => 'access', 'trunk' => 'hybrid');
1036 $ret .= "interface ${cmd['arg1']}\nport link-type " . $modemap[$cmd['arg2']] . "\n";
1037 if ($cmd['arg2'] == 'hybrid')
1038 $ret .= "undo port default vlan\nundo port trunk allow-pass vlan all\n";
1039 $ret .= "quit\n";
1040 break;
1041 case 'begin configuration':
1042 $ret .= "system-view\n";
1043 break;
1044 case 'end configuration':
1045 $ret .= "return\n";
1046 break;
1047 case 'save configuration':
1048 $ret .= "save\nY\n";
1049 break;
1050 default:
1051 throw new InvalidArgException ('opcode', $cmd['opcode']);
1052 }
1053 return $ret;
1054}
1055
1056function vrp55TranslatePushQueue ($queue)
1057{
1058 $ret = '';
1059 foreach ($queue as $cmd)
1060 switch ($cmd['opcode'])
1061 {
1062 case 'create VLAN':
1063 if ($cmd['arg1'] != 1)
1064 $ret .= "vlan ${cmd['arg1']}\nquit\n";
1065 break;
1066 case 'destroy VLAN':
1067 if ($cmd['arg1'] != 1)
1068 $ret .= "undo vlan ${cmd['arg1']}\n";
1069 break;
1070 case 'add allowed':
1071 case 'rem allowed':
1072 $undo = $cmd['opcode'] == 'add allowed' ? '' : 'undo ';
1073 $ret .= "interface ${cmd['port']}\n";
1074 foreach (listToRanges ($cmd['vlans']) as $range)
1075 $ret .= "${undo}port trunk allow-pass vlan " .
1076 ($range['from'] == $range['to'] ? $range['to'] : "${range['from']} to ${range['to']}") .
1077 "\n";
1078 $ret .= "quit\n";
1079 break;
1080 case 'set native':
1081 $ret .= "interface ${cmd['arg1']}\nport trunk pvid vlan ${cmd['arg2']}\nquit\n";
1082 break;
1083 case 'set access':
1084 $ret .= "interface ${cmd['arg1']}\nport default vlan ${cmd['arg2']}\nquit\n";
1085 break;
1086 case 'unset native':
1087 $ret .= "interface ${cmd['arg1']}\nundo port trunk pvid vlan\nquit\n";
1088 break;
1089 case 'unset access':
1090 $ret .= "interface ${cmd['arg1']}\nundo port default vlan\nquit\n";
1091 break;
1092 case 'set mode':
1093 // VRP 5.50's meaning of "trunk" is much like the one of IOS
1094 // (unlike the way VRP 5.30 defines "trunk" and "hybrid"),
1095 // but it is necessary to undo configured VLANs on a port
1096 // for mode change command to succeed.
1097 $undo = array
1098 (
1099 'access' => "undo port trunk allow-pass vlan all\n" .
1100 "port trunk allow-pass vlan 1\n" .
1101 "undo port trunk pvid vlan\n",
1102 'trunk' => "undo port default vlan\n",
1103 );
1104 $ret .= "interface ${cmd['arg1']}\n" . $undo[$cmd['arg2']];
1105 $ret .= "port link-type ${cmd['arg2']}\nquit\n";
1106 break;
1107 case 'begin configuration':
1108 $ret .= "system-view\n";
1109 break;
1110 case 'end configuration':
1111 $ret .= "return\n";
1112 break;
1113 case 'save configuration':
1114 $ret .= "save\nY\n";
1115 break;
1116 default:
1117 throw new InvalidArgException ('opcode', $cmd['opcode']);
1118 }
1119 return $ret;
1120}
1121
1122function xos12TranslatePushQueue ($queue)
1123{
1124 $ret = '';
1125 foreach ($queue as $cmd)
1126 switch ($cmd['opcode'])
1127 {
1128 case 'create VLAN':
1129 $ret .= "create vlan VLAN${cmd['arg1']}\n";
1130 $ret .= "configure vlan VLAN${cmd['arg1']} tag ${cmd['arg1']}\n";
1131 break;
1132 case 'destroy VLAN':
1133 $ret .= "delete vlan VLAN${cmd['arg1']}\n";
1134 break;
1135 case 'add allowed':
1136 foreach ($cmd['vlans'] as $vlan_id)
1137 {
1138 $vlan_name = $vlan_id == 1 ? 'Default' : "VLAN${vlan_id}";
1139 $ret .= "configure vlan ${vlan_name} add ports ${cmd['port']} tagged\n";
1140 }
1141 break;
1142 case 'rem allowed':
1143 foreach ($cmd['vlans'] as $vlan_id)
1144 {
1145 $vlan_name = $vlan_id == 1 ? 'Default' : "VLAN${vlan_id}";
1146 $ret .= "configure vlan ${vlan_name} delete ports ${cmd['port']}\n";
1147 }
1148 break;
1149 case 'set native':
1150 $vlan_name = $cmd['arg2'] == 1 ? 'Default' : "VLAN${cmd['arg2']}";
1151 $ret .= "configure vlan ${vlan_name} delete ports ${cmd['arg1']}\n";
1152 $ret .= "configure vlan ${vlan_name} add ports ${cmd['arg1']} untagged\n";
1153 break;
1154 case 'unset native':
1155 $vlan_name = $cmd['arg2'] == 1 ? 'Default' : "VLAN${cmd['arg2']}";
1156 $ret .= "configure vlan ${vlan_name} delete ports ${cmd['arg1']}\n";
1157 $ret .= "configure vlan ${vlan_name} add ports ${cmd['arg1']} tagged\n";
1158 break;
1159 case 'set access':
1160 $vlan_name = $cmd['arg2'] == 1 ? 'Default' : "VLAN${cmd['arg2']}";
1161 $ret .= "configure vlan ${vlan_name} add ports ${cmd['arg1']} untagged\n";
1162 break;
1163 case 'unset access':
1164 $vlan_name = $cmd['arg2'] == 1 ? 'Default' : "VLAN${cmd['arg2']}";
1165 $ret .= "configure vlan ${vlan_name} delete ports ${cmd['arg1']}\n";
1166 break;
1167 case 'set mode':
1168 case 'begin configuration':
1169 case 'end configuration':
1170 break; // NOP
1171 case 'save configuration':
1172 $ret .= "save configuration\ny\n";
1173 break;
1174 default:
1175 throw new InvalidArgException ('opcode', $cmd['opcode']);
1176 }
1177 return $ret;
1178}
1179
8d6c3668
AA
1180function jun10TranslatePushQueue ($queue, $vlan_names)
1181{
1182 $ret = '';
1183 if (! isset ($vlan_names))
1184 $vlan_names = array();
1185
1186 foreach ($queue as $cmd)
1187 switch ($cmd['opcode'])
1188 {
1189 case 'create VLAN':
1190 $ret .= "set vlans VLAN${cmd['arg1']} vlan-id ${cmd['arg1']}\n";
1191 break;
1192 case 'destroy VLAN':
1193 if (isset ($vlan_names[$cmd['arg1']]))
1194 $ret .= "delete vlans " . $vlan_names[$cmd['arg1']] . "\n";
1195 break;
1196 case 'add allowed':
1197 case 'rem allowed':
1198 $del = ($cmd['opcode'] == 'rem allowed');
1199 $pre = ($del ? 'delete' : 'set') .
1200 " interfaces ${cmd['port']} unit 0 family ethernet-switching vlan members";
1201 if (count ($cmd['vlans']) > VLAN_MAX_ID - VLAN_MIN_ID)
1202 $ret .= "$pre " . ($del ? '' : 'all') . "\n";
1203 else
1204 while (! empty ($cmd['vlans']))
1205 {
1206 $vlan = array_shift ($cmd['vlans']);
1207 $ret .= "$pre $vlan\n";
1208 if ($del and isset ($vlan_names[$vlan]))
1209 $ret .= "$pre ${vlan_names[$vlan]}\n";
1210 }
1211 break;
1212 case 'set native':
1213 $ret .= "set interfaces ${cmd['arg1']} unit 0 family ethernet-switching native-vlan-id ${cmd['arg2']}\n";
1214 break;
1215 case 'unset native':
1216 $ret .= "delete interfaces ${cmd['arg1']} unit 0 family ethernet-switching native-vlan-id\n";
1217 break;
1218 case 'set access':
1219 $ret .= "set interfaces ${cmd['arg1']} unit 0 family ethernet-switching vlan members ${cmd['arg2']}\n";
1220 break;
1221 case 'unset access':
1222 $ret .= "delete interfaces ${cmd['arg1']} unit 0 family ethernet-switching vlan members\n";
1223 break;
1224 case 'set mode':
1225 $ret .= "set interfaces ${cmd['arg1']} unit 0 family ethernet-switching port-mode ${cmd['arg2']}\n";
1226 break;
1227 case 'begin configuration':
1228 $ret .= "configure exclusive\n";
1229 break;
1230 case 'end configuration':
1231 $ret .= "commit confirmed 120\n";
1232 break;
1233 case 'save configuration':
1234 break; // JunOS can`t apply configuration without saving it
1235 default:
1236 throw new InvalidArgException ('opcode', $cmd['opcode']);
1237 }
1238 return $ret;
1239}
1240
b519e37e
DO
1241function xos12Read8021QConfig ($input)
1242{
1243 $ret = array
1244 (
1245 'vlanlist' => array (1),
1246 'portdata' => array(),
1247 );
1248 foreach (explode ("\n", $input) as $line)
1249 {
1250 $matches = array();
1251 switch (TRUE)
1252 {
1253 case (preg_match ('/^create vlan "([[:alnum:]]+)"$/', $line, $matches)):
1254 if (!preg_match ('/^VLAN[[:digit:]]+$/', $matches[1]))
1255 throw new RTGatewayError ('unsupported VLAN name ' . $matches[1]);
1256 break;
1257 case (preg_match ('/^configure vlan ([[:alnum:]]+) tag ([[:digit:]]+)$/', $line, $matches)):
1258 if (strtolower ($matches[1]) == 'default')
1259 throw new RTGatewayError ('default VLAN tag must be 1');
1260 if ($matches[1] != 'VLAN' . $matches[2])
1261 throw new RTGatewayError ("VLAN name ${matches[1]} does not match its tag ${matches[2]}");
1262 $ret['vlanlist'][] = $matches[2];
1263 break;
1264 case (preg_match ('/^configure vlan ([[:alnum:]]+) add ports (.+) (tagged|untagged) */', $line, $matches)):
1265 $submatch = array();
1266 if ($matches[1] == 'Default')
1267 $matches[1] = 'VLAN1';
1268 if (!preg_match ('/^VLAN([[:digit:]]+)$/', $matches[1], $submatch))
1269 throw new RTGatewayError ('unsupported VLAN name ' . $matches[1]);
1270 $vlan_id = $submatch[1];
1271 foreach (iosParseVLANString ($matches[2]) as $port_name)
1272 {
1273 if (!array_key_exists ($port_name, $ret['portdata']))
1274 $ret['portdata'][$port_name] = array
1275 (
1276 'mode' => 'trunk',
1277 'allowed' => array(),
1278 'native' => 0,
1279 );
1280 $ret['portdata'][$port_name]['allowed'][] = $vlan_id;
1281 if ($matches[3] == 'untagged')
1282 $ret['portdata'][$port_name]['native'] = $vlan_id;
1283 }
1284 break;
1285 default:
1286 }
1287 }
1288 return $ret;
1289}
1290
8d6c3668
AA
1291function jun10Read8021QConfig ($input)
1292{
1293 $ret = array
1294 (
1295 'vlanlist' => array (1),
1296 'vlannames' => array (1 => 'default'),
1297 'portdata' => array(),
1298 );
1299 $lines = explode ("\n", $input);
1300
1301 // get vlan list
1302 $vlans = array('default' => 1);
1303 $names = array();
1304 while (count ($lines))
1305 {
1306 $line = trim (array_shift ($lines));
1307 if (FALSE !== strpos ($line, '# END OF VLAN LIST'))
1308 break;
1309 if (preg_match ('/^VLAN: (.*), 802.1Q Tag: (\d+)/', $line, $m))
1310 {
1311 $ret['vlannames'][$m[2]] = $m[1];
1312 $vlans[$m[1]] = $m[2];
1313 }
1314 }
1315 $ret['vlanlist'] = array_values ($vlans);
1316
1317 // get config groups list - throw an exception if a group contains ether-switching config
1318 $current_group = NULL;
1319 while (count ($lines))
1320 {
1321 $line = array_shift ($lines);
1322 if (FALSE !== strpos ($line, '# END OF GROUP LIST'))
1323 break;
1324 elseif (preg_match ('/^(\S+)(?:\s+{|;)$/', $line, $m))
1325 $current_group = $m[1];
1326 elseif (isset ($current_group) and preg_match ('/^\s*family ethernet-switching\b/', $line))
1327 throw new RTGatewayError ("Config-group '$current_group' contains switchport commands, which is not supported");
1328 }
1329
1330 // get interfaces config
1331 $current = array
1332 (
1333 'is_range' => FALSE,
1334 'is_ethernet' => FALSE,
1335 'name' => NULL,
1336 'config' => NULL,
1337 'indent' => NULL,
1338 );
1339 while (count ($lines))
1340 {
1341 $line = array_shift ($lines);
1342 if (preg_match ('/# END OF CONFIG|^(interface-range )?(\S+)\s+{$/', $line, $m)) // line starts with interface name
1343 { // found interface section opening, or end-of-file
1344 if (isset ($current['name']) and $current['is_ethernet'])
1345 {
1346 // add previous interface to the results
1347 if (! isset ($current['config']['mode']))
1348 $current['config']['mode'] = 'access';
1349 if (! isset ($current['config']['native']))
1350 $current['config']['native'] = $current['config']['native'] = 0;
1351 if (! isset ($current['config']['allowed']))
1352 {
1353 if ($current['config']['mode'] == 'access')
1354 $current['config']['allowed'] = array (1);
1355 else
1356 $current['config']['allowed'] = array();
1357 }
1358 if (
1359 $current['config']['mode'] == 'trunk' and
1360 $current['config']['native'] != 0 and
1361 ! in_array ($current['config']['native'], $current['config']['allowed'])
1362 )
1363 $current['config']['allowed'][] = $current['config']['native'];
1364 elseif ($current['config']['mode'] == 'access')
1365 $current['config']['native'] = $current['config']['allowed'][0];
1366 $ret['portdata'][$current['name']] = $current['config'];
1367 }
1368
1369 $current['is_ethernet'] = FALSE;
1370 $current['is_range'] = (TRUE == $m[1]);
1371 $current['name'] = $m[2];
1372 $current['config'] = array (
1373 'mode' => NULL,
1374 'allowed' => NULL,
1375 'native' => NULL,
1376 );
1377 $current['indent'] = NULL;
1378 }
1379 elseif (preg_match ('/^(\s+)family ethernet-switching\b/', $line, $m))
1380 {
1381 if ($current['is_range'])
1382 throw new RTGatewayError ("interface-range '${current['name']}' contains switchport commands, which is not supported");
1383 $current['is_ethernet'] = TRUE;
1384 $current['indent'] = $m[1];
1385 }
1386 elseif (isset ($current['indent']) and $line == $config_indent . '}')
1387 $current['indent'] = NULL;
1388 elseif ($current['is_ethernet'] and isset ($current['indent']))
1389 {
1390 if (preg_match ('/^\s+port-mode (trunk|access);/', $line, $m))
1391 $current['config']['mode'] = $m[1];
1392 elseif (preg_match ('/^\s+native-vlan-id (\d+);/', $line, $m))
1393 $current['config']['native'] = $m[1];
1394 elseif (preg_match ('/^\s+members \[?(.*)\]?;$/', $line, $m))
1395 {
1396 $members = array();
1397 foreach (explode (' ', $m[1]) as $item)
1398 {
1399 $item = trim ($item);
1400 if (preg_match ('/^(\d+)(?:-(\d+))?$/', $item, $m))
1401 {
1402 if (isset ($m[2]) and $m[2] > $m[1])
1403 $members = array_merge (range ($m[1], $m[2]), $members);
1404 else
1405 $members[] = $m[1];
1406 }
1407 elseif (isset ($vlans[$item]))
1408 $members[] = $vlans[$item];
1409 elseif ($item == 'all')
1410 $members = array_merge (range (VLAN_MIN_ID, VLAN_MAX_ID), $members);
1411 }
1412 $current['config']['allowed'] = array_unique ($members);
1413 }
1414 }
1415 }
1416
1417 return $ret;
1418}
1419
b519e37e
DO
1420function ciscoReadInterfaceStatus ($text)
1421{
1422 $result = array();
1423 $state = 'headerSearch';
1424 foreach (explode ("\n", $text) as $line)
1425 {
1426 switch ($state)
1427 {
1428 case 'headerSearch':
1429 if (preg_match('/^Port\s+Name\s+Status/', $line))
1430 {
1431 $name_field_borders = getColumnCoordinates($line, 'Name');
1432 if (isset ($name_field_borders['from']))
1433 $state = 'readPort';
1434 }
1435 break;
1436 case 'readPort':
1437 $portname = ios12ShortenIfName (trim (substr ($line, 0, $name_field_borders['from'])));
1438 $rest = trim (substr ($line, $name_field_borders['from'] + $name_field_borders['length'] + 1));
1439 $field_list = preg_split('/\s+/', $rest);
1440 if (count ($field_list) < 4)
1441 break;
1442 list ($status_raw, $vlan, $duplex, $speed, $type) = $field_list;
1443 if ($status_raw == 'connected' || $status_raw == 'up')
1444 $status = 'up';
1445 elseif ($status_raw == 'notconnect' || $status_raw == 'down')
1446 $status = 'down';
1447 else
1448 $status = 'disabled';
1449 $result[$portname] = array
1450 (
1451 'status' => $status,
1452 'speed' => $speed,
1453 'duplex' => $duplex,
1454 );
1455 break;
1456 }
1457 }
1458 return $result;
1459}
1460
1461function vrpReadInterfaceStatus ($text)
1462{
1463 $result = array();
1464 $state = 'headerSearch';
1465 foreach (explode ("\n", $text) as $line)
1466 {
1467 switch ($state)
1468 {
1469 case 'headerSearch':
1470 if (preg_match('/^Interface\s+Phy\w*\s+Protocol/i', $line))
1471 $state = 'readPort';
1472 break;
1473 case 'readPort':
1474 if (preg_match('/[\$><\]]/', $line))
1475 break 2;
1476 $field_list = preg_split('/\s+/', $line);
1477 if (count ($field_list) < 7)
1478 break;
1479 list ($portname, $status_raw) = $field_list;
1480 $portname = ios12ShortenIfName ($portname);
1481
1482 if ($status_raw == 'up' || $status_raw == 'down')
1483 $status = $status_raw;
1484 else
1485 $status = 'disabled';
1486 $result[$portname] = array
1487 (
1488 'status' => $status,
1489 );
1490 break;
1491 }
1492 }
1493 return $result;
1494}
1495
1496function maclist_sort ($a, $b)
1497{
1498 if ($a['vid'] == $b['vid'])
1499 return 0;
1500 return ($a['vid'] < $b['vid']) ? -1 : 1;
1501}
1502
1503function ios12ReadMacList ($text)
1504{
1505 $result = array();
1506 $state = 'headerSearch';
1507 foreach (explode ("\n", $text) as $line)
1508 {
1509 switch ($state)
1510 {
1511 case 'headerSearch':
1512 if (preg_match('/Vlan\s+Mac Address\s+Type.*Ports?\s*$/i', $line))
1513 $state = 'readPort';
1514 break;
1515 case 'readPort':
1516 if (! preg_match ('/(\d+)\s+([a-f0-9]{4}\.[a-f0-9]{4}\.[a-f0-9]{4})\s.*?(\S+)$/', trim ($line), $matches))
1517 break;
1518 $portname = ios12ShortenIfName ($matches[3]);
1519 $result[$portname][] = array
1520 (
1521 'mac' => $matches[2],
1522 'vid' => $matches[1],
1523 );
1524 break;
1525 }
1526 }
1527 foreach ($result as $portname => &$maclist)
1528 usort ($maclist, 'maclist_sort');
1529 return $result;
1530}
1531
1532function nxos4ReadMacList ($text)
1533{
1534 $result = array();
1535 $state = 'headerSearch';
1536 foreach (explode ("\n", $text) as $line)
1537 {
1538 switch ($state)
1539 {
1540 case 'headerSearch':
1541 if (preg_match('/VLAN\s+MAC Address\s+Type\s+age\s+Secure\s+NTFY\s+Ports/i', $line))
1542 $state = 'readPort';
1543 break;
1544 case 'readPort':
1545 if (! preg_match ('/(\d+)\s+([a-f0-9]{4}\.[a-f0-9]{4}\.[a-f0-9]{4})\s.*?(\S+)$/', trim ($line), $matches))
1546 break;
1547 $portname = ios12ShortenIfName ($matches[3]);
1548 $result[$portname][] = array
1549 (
1550 'mac' => $matches[2],
1551 'vid' => $matches[1],
1552 );
1553 break;
1554 }
1555 }
1556 foreach ($result as $portname => &$maclist)
1557 usort ($maclist, 'maclist_sort');
1558 return $result;
1559}
1560
1561function vrp53ReadMacList ($text)
1562{
1563 $result = array();
1564 $state = 'headerSearch';
1565 foreach (explode ("\n", $text) as $line)
1566 {
1567 switch ($state)
1568 {
1569 case 'headerSearch':
1570 if (preg_match('/MAC Address\s+VLAN\/VSI\s+Port/i', $line))
1571 $state = 'readPort';
1572 break;
1573 case 'readPort':
1574 if (! preg_match ('/([a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4})\s+(\d+)\s+(\S+)/', trim ($line), $matches))
1575 break;
1576 $portname = ios12ShortenIfName ($matches[3]);
1577 $result[$portname][] = array
1578 (
1579 'mac' => str_replace ('-', '.', $matches[1]),
1580 'vid' => $matches[2],
1581 );
1582 break;
1583 }
1584 }
1585 foreach ($result as $portname => &$maclist)
1586 usort ($maclist, 'maclist_sort');
1587 return $result;
1588}
1589
1590function vrp55ReadMacList ($text)
1591{
1592 $result = array();
1593 $state = 'headerSearch';
1594 foreach (explode ("\n", $text) as $line)
1595 {
1596 switch ($state)
1597 {
1598 case 'headerSearch':
1599 if (preg_match('/MAC Address\s+VLAN\/\S*\s+PEVLAN\s+CEVLAN\s+Port/i', $line))
1600 $state = 'readPort';
1601 break;
1602 case 'readPort':
1603 if (! preg_match ('/([a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4})\s+(\d+)(?:\s+\S+){2}\s+(\S+)/', trim ($line), $matches))
1604 break;
1605 $portname = ios12ShortenIfName ($matches[3]);
1606 $result[$portname][] = array
1607 (
1608 'mac' => str_replace ('-', '.', $matches[1]),
1609 'vid' => $matches[2],
1610 );
1611 break;
1612 }
1613 }
1614 foreach ($result as $portname => &$maclist)
1615 usort ($maclist, 'maclist_sort');
1616 return $result;
1617}
1618
1619?>