1 /*
2  */
3 
4 #include "graph.h"
5 
6 /******************************************************************************
7 Match physical interfaces between equipements (i.e. physical links)
8 ******************************************************************************/
9 
l1graph(void)10 void l1graph (void)
11 {
12     struct node *n ;
13     struct linklist *physlist ;		/* head of physical list */
14     struct linklist *l ;
15 
16     /*
17      * First, locate all L1 nodes to extract physical links and create
18      * an appropriate structure, referenced by head of list physlist.
19      */
20 
21     physlist = NULL ;
22     for (n = mobj_head (nodemobj) ; n != NULL ; n = n->next)
23     {
24 	if (n->nodetype == NT_L1 && strcmp (n->u.l1.link, EXTLINK) != 0)
25 	{
26 	    struct link *pl ;
27 	    struct linklist *ll ;
28 	    struct symtab *s ;
29 
30 	    s = symtab_get (n->u.l1.link) ;
31 	    pl = symtab_to_link (s) ;
32 	    if (pl == NULL)
33 	    {
34 		/* never seen this link : initialize it */
35 		pl = symtab_to_link (s) = mobj_alloc (linkmobj, 1) ;
36 		pl->name = symtab_to_name (s) ;
37 		pl->node [0] = n ;
38 		pl->node [1] = NULL ;
39 
40 		ll = mobj_alloc (llistmobj, 1) ;
41 		ll->link = pl ;
42 		ll->next = physlist ;
43 		physlist = ll ;
44 	    }
45 	    else
46 	    {
47 		if (pl->node [1] == NULL)
48 		{
49 		    /* this is the other end of the link */
50 		    pl->node [1] = n ;
51 		}
52 		else
53 		{
54 		    /* a link with more than two endpoints... */
55 		    inconsistency ("Link '%s' has two many endpoints",
56 						n->u.l1.link) ;
57 		}
58 	    }
59 	}
60     }
61 
62     /*
63      * Next, connect all links to nodes linklists
64      */
65 
66     l = physlist ;
67     while (l != NULL)
68     {
69 	struct linklist *llnext ;
70 
71 	llnext = l->next ;
72 
73 	if (l->link->node [1] == NULL)
74 	    inconsistency ("Link '%s' seen only on one node (%s)",
75 					l->link->name,
76 					l->link->node [0]->eq->name) ;
77 	else /* the link has two endpoints */
78 	    (void) create_link (l->link->name,
79 					l->link->node [0]->name,
80 					l->link->node [1]->name) ;
81 
82 	mobj_free (linkmobj, l->link) ;
83 	mobj_free (llistmobj, l) ;
84 	l = llnext ;
85     }
86 }
87 
88 /******************************************************************************
89 Expands L2PAT nodes at edge of our graph, such as these new L2 nodes become
90 producers of Vlans
91 ******************************************************************************/
92 
93 /*
94  * limit2pat : limit l2pat expansion to this threshold (-l option)
95  * allvlans : don't limit l2pat expansion to named vlans (-a option)
96  */
97 
l1prodl2pat(int verbose,int limitl2pat,int allvlans)98 void l1prodl2pat (int verbose, int limitl2pat, int allvlans)
99 {
100     struct node *n ;
101     struct linklist *ll ;
102     int nl2pat, nl2 ;
103     struct vlan *tabvlan ;
104 
105     nl2pat = nl2 = 0 ;
106 
107     tabvlan = mobj_data (vlanmobj) ;
108 
109     for (n = mobj_head (nodemobj) ; n != NULL ; n = n->next)
110     {
111 	if (n->nodetype == NT_L1 && strcmp (n->u.l1.link, EXTLINK) == 0)
112 	{
113 	    struct node *l2pat ;
114 
115 	    l2pat = get_neighbour (n, NT_L2PAT) ;
116 	    if (l2pat != NULL)
117 	    {
118 		struct vlanlist *a ;
119 		int thisl2pat = 0 ;
120 
121 		/*
122 		 * Instantiate this L2pat into each allowed vlan
123 		 */
124 
125 		nl2pat++ ;				/* verbose stats */
126 
127 		for (a = l2pat->u.l2pat.allowed ; a != NULL ; a = a->next)
128 		{
129 		    int v ;
130 
131 		    for (v = a->min ; v <= a->max ; v++)
132 		    {
133 			struct node *l2 ;
134 
135 			if (allvlans || tabvlan [v].name != NULL)
136 			{
137 			    l2 = create_node (new_nodename (l2pat->eq->name),
138 				l2pat->eq, NT_L2) ;
139 			    l2->u.l2.vlan = v ;
140 			    l2->u.l2.stat = NULL ;
141 			    l2->u.l2.native = (l2pat->u.l2pat.native == v) ; ;
142 
143 			    nl2++ ;				/* verbose stats */
144 			    thisl2pat++ ;			/* # of expanded L2 for this L2pat */
145 
146 			    for (ll = l2pat->linklist ; ll != NULL ; ll = ll->next)
147 			    {
148 				struct link *l ;
149 				struct node *o ;		/* other node */
150 
151 				l = ll->link ;
152 				o = getlinkpeer (l, l2pat) ;
153 
154 				(void) create_link (NULL, o->name, l2->name) ;
155 			    }
156 			}
157 		    }
158 		}
159 
160 		if (limitl2pat > 0 && thisl2pat >= limitl2pat)
161 		{
162 		    fprintf (stderr, "%s/%s : %d vlans expanded\n",
163 				n->eq->name,
164 				n->u.l1.ifname,
165 				thisl2pat) ;
166 		}
167 
168 		/*
169 		 * This L2pat is no longer needed.
170 		 * Removing it is too complex.
171 		 * We just remove all allowed vlans
172 		 * This is a memory leak (the vlanlist), but this is not
173 		 * important since this memory block will not be saved
174 		 * in the generated graph.
175 		 */
176 
177 		l2pat->u.l2pat.allowed = NULL ;
178 	    }
179 	}
180     }
181 
182     if (verbose)
183 	fprintf (stderr, "l1prodl2pat : %d L2PAT expanded in %d L2\n",
184 				nl2pat, nl2) ;
185 }
186 
187 /******************************************************************************
188 Computes the list of Vlan-ids transported on each link
189 ******************************************************************************/
190 
l2graph(void)191 void l2graph (void)
192 {
193     struct node *n ;
194     vlanset_t verr ;
195     struct node *ref [MAXVLAN] ;
196     int i ;
197 
198     for (i = 0 ; i < MAXVLAN ; i++)
199 	ref [i] = NULL ;
200 
201     for (n = mobj_head (nodemobj) ; n != NULL ; n = n->next)
202 	vlan_zero (n->vlanset) ;
203     vlan_zero (verr) ;			/* vlan for which we already reported an error */
204 
205     for (n = mobj_head (nodemobj) ; n != NULL ; n = n->next)
206     {
207 	if (n->nodetype == NT_L2 && ! vlan_isset (n->vlanset, n->u.l2.vlan))
208 	{
209 	    vlan_t v ;
210 	    struct node *l1 ;
211 
212 	    l1 = get_neighbour (n, NT_L1) ;
213 
214 	    v = n->u.l2.vlan ;
215 	    if (v > 1 && ref [v] != NULL && ! vlan_isset (verr, v))
216 	    {
217 		inconsistency ("Vlan '%d' disconnected between %s:%s and %s:%s",
218 				v, ref [v]->eq->name, ref [v]->u.l1.ifname,
219 				n->eq->name, ((l1!=NULL) ? l1->u.l1.ifname : "?")) ;
220 		vlan_set (verr, v) ;
221 	    }
222 	    else ref [v] = l1 ;
223 
224 	    transport_vlan_on_L2 (n, v) ;
225 	}
226     }
227 }
228 
229 
230 /******************************************************************************
231 Update active vlans on each equipement
232 ******************************************************************************/
233 
update_lvlans(void)234 void update_lvlans (void)
235 {
236     struct vlan *tabvlan ;
237     struct node *n ;
238     struct eq *eq ;
239     struct lvlan *lv ;
240     int i ;
241 
242     tabvlan = mobj_data (vlanmobj) ;
243 
244     for (i = 1 ; i < MAXVLAN ; i++)
245     {
246 	for (eq = mobj_head (eqmobj) ; eq != NULL ; eq = eq->next)
247 	    eq->mark = 0 ;
248 
249 	for (n = mobj_head (nodemobj) ; n != NULL ; n = n->next)
250 	    if (n->nodetype == NT_L2 && vlan_isset (n->vlanset, i))
251 		n->eq->mark = 1 ;
252 
253 	for (eq = mobj_head (eqmobj) ; eq != NULL ; eq = eq->next)
254 	{
255 	    if (eq->mark)
256 	    {
257 		for (lv = tabvlan [i].lvlan ; lv != NULL ; lv = lv->next)
258 		    if (lv->eq == eq)
259 			break ;
260 
261 		if (lv == NULL)
262 		{
263 		    lv = mobj_alloc (lvlanmobj, 1) ;
264 		    lv->next = tabvlan [i].lvlan ;
265 		    tabvlan [i].lvlan = lv ;
266 		    lv->eq = eq ;
267 		    lv->vlanid = i ;
268 		    lv->name = NULL ;
269 		    lv->mark = 0 ;
270 		}
271 
272 		lv->mark |= LVLAN_INCOMING ;
273 	    }
274 	}
275     }
276 }
277 
278 
279 /******************************************************************************
280 Inconsistency checking
281 ******************************************************************************/
282 
check_inconsistencies(void)283 void check_inconsistencies (void)
284 {
285 }
286 
287 /******************************************************************************
288 Removes L2PAT and BRPAT nodes, and links
289 ******************************************************************************/
290 
remove_link_to_me(struct node * from,struct node * me)291 void remove_link_to_me (struct node *from, struct node *me)
292 {
293     struct node *other ;
294     struct linklist *ll, *llprev, *llnext ;
295 
296     llprev = NULL ;
297     ll = from->linklist ;
298     while (ll != NULL)
299     {
300 	llnext = ll->next ;
301 
302 	other = getlinkpeer (ll->link, from) ;
303 	if (other == me)
304 	{
305 	    /* Remove only the linklist entry. */
306 	    mobj_free (llistmobj, ll) ;
307 	    if (llprev == NULL)
308 		from->linklist = llnext ;
309 	    else
310 		llprev->next = llnext ;
311 	}
312 	else llprev = ll ;
313 	ll = llnext ;
314     }
315 }
316 
remove_l2pat_brpat(void)317 void remove_l2pat_brpat (void)
318 {
319     struct node *n, *nprev, *nnext, *other, *enext, *eprev;
320     struct linklist *ll, *llnext ;
321 
322     nprev = NULL ;
323     n = mobj_head (nodemobj) ;
324     while (n != NULL)
325     {
326 	nnext = n->next ;
327 
328 	if (n->nodetype == NT_L2PAT || n->nodetype == NT_BRPAT)
329 	{
330 	    /*
331 	     * Remove all links starting from this node, and
332 	     * symmetrical links.
333 	     */
334 
335 	    ll = n->linklist ;
336 	    while (ll != NULL)
337 	    {
338 		llnext = ll->next ;
339 
340 		/*
341 		 * Remove only the linklist entries pointing to us
342 		 * The link entry is shared, we will remove it later
343 		 * (in a few lines)
344 		 */
345 
346 		other = getlinkpeer (ll->link, n) ;
347 		remove_link_to_me (other, n) ;
348 
349 		/*
350 		 * Remove the link and the linklist entries.
351 		 */
352 
353 		mobj_free (linkmobj, ll->link) ;
354 		mobj_free (llistmobj, ll) ;
355 
356 		ll = llnext ;
357 	    }
358 
359 	    /*
360 	     * Next in node list
361 	     */
362 
363 	    if (nprev == NULL)
364 		mobj_sethead (nodemobj, nnext) ;
365 	    else
366 		nprev->next = nnext ;
367 
368 	    /*
369 	     * Modify equipment node list accordingly
370 	     */
371 
372 	    if ( (enext = n->enext) != NULL)
373 	    {
374 		enext->eprev = n->eprev;
375 		if (n->eq->enhead == n)
376 		{
377 		    n->eq->enhead = enext;
378 		    enext->eprev = NULL;
379 		}
380 	    }
381 	    if ( (eprev = n->eprev) != NULL)
382 	    {
383 		eprev->enext = n->enext;
384 		if (n->eq->entail == n) {
385 		    n->eq->enhead = eprev;
386 		    eprev->enext = NULL;
387 		}
388 	    }
389 
390 	    mobj_free (nodemobj, n) ;
391 	}
392 	else nprev = n ;
393 
394 	n = nnext ;
395     }
396 }
397 
398 /******************************************************************************
399 Attach network addresses to vlans
400 ******************************************************************************/
401 
add_net_to_vlan(vlan_t vlan,ip_t * addr_in_net)402 void add_net_to_vlan (vlan_t vlan, ip_t *addr_in_net)
403 {
404     ip_t net ;
405     struct netlist *nl ;
406     struct vlan *tab ;
407     struct network *n ;
408 
409     tab = mobj_data (vlanmobj) ;
410     ip_netof (addr_in_net, &net) ;
411     n = net_get_n (&net) ;
412     for (nl = tab [vlan].netlist ; nl != NULL ; nl = nl->next)
413 	if (&nl->net->addr == &n->addr)	/* pointer comparison */
414 	    break ;
415     if (nl == NULL)
416     {
417 	nl = mobj_alloc (nlistmobj, 1) ;
418 	nl->net = n ;
419 	nl->next = tab [vlan].netlist ;
420 	tab [vlan].netlist = nl ;
421     }
422 }
423 
attach_net_to_vlan(void)424 void attach_net_to_vlan (void)
425 {
426     struct node *n ;
427     int vlan ;
428 
429     for (vlan = 2 ; vlan < MAXVLAN ; vlan++)
430 	for (n = mobj_head (nodemobj) ; n != NULL ; n = n->next)
431 	    if (n->nodetype == NT_L3 && vlan_isset (n->vlanset, vlan))
432 		add_net_to_vlan (vlan, &n->u.l3.addr) ;
433 }
434 
435 
436 /******************************************************************************
437 Main function
438 ******************************************************************************/
439 
440 MOBJ *newmobj [NB_MOBJ] ;
441 
usage(char * progname)442 void usage (char *progname)
443 {
444     fprintf (stderr, "Usage : %s [-l <threshold>] [-n] [-v]\n", progname) ;
445     fprintf (stderr, "\t-a           : all vlans (including unnamed vlans)\n") ;
446     fprintf (stderr, "\t-l threshold : vlan threshold for node display\n") ;
447     fprintf (stderr, "\t-v           : verbose\n") ;
448     exit (1) ;
449 }
450 
main(int argc,char * argv[])451 int main (int argc, char *argv [])
452 {
453     char *prog ;
454     int c, err ;
455     int verbose, allvlans, limitl2pat ;
456 
457     prog = argv [0] ;
458     err = 0 ;
459     verbose = 0 ;
460     limitl2pat = 0 ;
461     allvlans = 0 ;
462 
463     while ((c = getopt (argc, argv, "al:v")) != -1)
464     {
465 	switch (c)
466 	{
467 	    case 'a' :
468 		allvlans = 1 ;
469 		break ;
470 	    case 'l' :
471 		limitl2pat = atoi (optarg) ;
472 		break ;
473 	    case 'v' :
474 		verbose = 1 ;
475 		break ;
476 	    case '?' :
477 	    default :
478 		usage (prog) ;
479 	}
480     }
481 
482     if (err)
483 	exit (1) ;
484 
485     argc -= optind ;
486     argv += optind ;
487 
488     if (argc != 0)
489 	usage (prog) ;
490 
491     text_read (stdin) ;
492     l1graph () ;
493     check_links () ;
494     l1prodl2pat (verbose, limitl2pat, allvlans) ;
495     l2graph () ;
496     update_lvlans () ;
497     check_inconsistencies () ;
498     remove_l2pat_brpat () ;
499     attach_net_to_vlan () ;
500     duplicate_graph (newmobj, mobjlist) ;
501     bin_write (stdout, newmobj) ;
502     exit (0) ;
503 }
504