1 |
/* |
2 |
* Cisco 7200 (Predator) simulation platform. |
3 |
* Copyright (c) 2006 Christophe Fillot (cf@utc.fr) |
4 |
* |
5 |
* Virtual Ethernet switch with VLAN/Trunk support. |
6 |
*/ |
7 |
|
8 |
#include <stdio.h> |
9 |
#include <stdlib.h> |
10 |
#include <string.h> |
11 |
#include <unistd.h> |
12 |
#include <pthread.h> |
13 |
#include <errno.h> |
14 |
#include <sys/select.h> |
15 |
#include <sys/time.h> |
16 |
#include <sys/types.h> |
17 |
#include <assert.h> |
18 |
|
19 |
#include "utils.h" |
20 |
#include "net.h" |
21 |
#include "registry.h" |
22 |
#include "net_io.h" |
23 |
#include "eth_switch.h" |
24 |
|
25 |
/* Debbuging message */ |
26 |
void ethsw_debug(ethsw_table_t *t,char *fmt,...) |
27 |
{ |
28 |
char module[128]; |
29 |
va_list ap; |
30 |
|
31 |
if (t->debug) { |
32 |
va_start(ap,fmt); |
33 |
snprintf(module,sizeof(module),"ETHSW %s",t->name); |
34 |
m_flog(log_file,module,fmt,ap); |
35 |
va_end(ap); |
36 |
} |
37 |
} |
38 |
|
39 |
|
40 |
/* Compute hash index on the specified MAC address and VLAN */ |
41 |
static inline u_int ethsw_hash_index(n_eth_addr_t *addr,u_int vlan_id) |
42 |
{ |
43 |
u_int h_index; |
44 |
|
45 |
h_index = (addr->eth_addr_byte[0] << 8) | addr->eth_addr_byte[1]; |
46 |
h_index ^= (addr->eth_addr_byte[2] << 8) | addr->eth_addr_byte[3]; |
47 |
h_index ^= (addr->eth_addr_byte[4] << 8) | addr->eth_addr_byte[5]; |
48 |
h_index ^= vlan_id; |
49 |
return(h_index & (ETHSW_HASH_SIZE - 1)); |
50 |
} |
51 |
|
52 |
/* Invalidate the whole MAC address table */ |
53 |
static void ethsw_invalidate(ethsw_table_t *t) |
54 |
{ |
55 |
memset(t->mac_addr_table,0,sizeof(t->mac_addr_table)); |
56 |
} |
57 |
|
58 |
/* Invalidate entry of the MAC address table referring to the specified NIO */ |
59 |
static void ethsw_invalidate_port(ethsw_table_t *t,netio_desc_t *nio) |
60 |
{ |
61 |
ethsw_mac_entry_t *entry; |
62 |
int i; |
63 |
|
64 |
for(i=0;i<ETHSW_HASH_SIZE;i++) { |
65 |
entry = &t->mac_addr_table[i]; |
66 |
if (entry->nio == nio) { |
67 |
entry->nio = NULL; |
68 |
entry->vlan_id = 0; |
69 |
} |
70 |
} |
71 |
} |
72 |
|
73 |
/* Push a 802.1Q tag */ |
74 |
static void dot1q_push_tag(m_uint8_t *pkt,ethsw_packet_t *sp,u_int vlan) |
75 |
{ |
76 |
n_eth_dot1q_hdr_t *hdr; |
77 |
|
78 |
memcpy(pkt,sp->pkt,(N_ETH_HLEN - 2)); |
79 |
|
80 |
hdr = (n_eth_dot1q_hdr_t *)pkt; |
81 |
hdr->type = htons(N_ETH_PROTO_DOT1Q); |
82 |
hdr->vlan_id = htons(sp->input_vlan); |
83 |
|
84 |
memcpy(pkt + sizeof(n_eth_dot1q_hdr_t), |
85 |
sp->pkt + (N_ETH_HLEN - 2), |
86 |
sp->pkt_len - (N_ETH_HLEN - 2)); |
87 |
} |
88 |
|
89 |
/* Pop a 802.1Q tag */ |
90 |
static void dot1q_pop_tag(m_uint8_t *pkt,ethsw_packet_t *sp) |
91 |
{ |
92 |
memcpy(pkt,sp->pkt,(N_ETH_HLEN - 2)); |
93 |
|
94 |
memcpy(pkt + (N_ETH_HLEN - 2), |
95 |
sp->pkt + sizeof(n_eth_dot1q_hdr_t), |
96 |
sp->pkt_len - sizeof(n_eth_dot1q_hdr_t)); |
97 |
} |
98 |
|
99 |
/* Input vector for ACCESS ports */ |
100 |
static void ethsw_iv_access(ethsw_table_t *t,ethsw_packet_t *sp, |
101 |
netio_desc_t *op) |
102 |
{ |
103 |
m_uint8_t pkt[ETHSW_MAX_PKT_SIZE+4]; |
104 |
|
105 |
switch(op->vlan_port_type) { |
106 |
/* Access -> Access: no special treatment */ |
107 |
case ETHSW_PORT_TYPE_ACCESS: |
108 |
netio_send(op,sp->pkt,sp->pkt_len); |
109 |
break; |
110 |
|
111 |
/* Access -> 802.1Q: push tag */ |
112 |
case ETHSW_PORT_TYPE_DOT1Q: |
113 |
/* |
114 |
* If the native VLAN of output port is the same as input, |
115 |
* forward the packet without adding the tag. |
116 |
*/ |
117 |
if (op->vlan_id == sp->input_vlan) { |
118 |
netio_send(op,sp->pkt,sp->pkt_len); |
119 |
} else { |
120 |
dot1q_push_tag(pkt,sp,op->vlan_id); |
121 |
netio_send(op,pkt,sp->pkt_len+4); |
122 |
} |
123 |
break; |
124 |
|
125 |
default: |
126 |
fprintf(stderr,"ethsw_iv_access: unknown port type %u\n", |
127 |
op->vlan_port_type); |
128 |
} |
129 |
} |
130 |
|
131 |
/* Input vector for 802.1Q ports */ |
132 |
static void ethsw_iv_dot1q(ethsw_table_t *t,ethsw_packet_t *sp, |
133 |
netio_desc_t *op) |
134 |
{ |
135 |
m_uint8_t pkt[ETHSW_MAX_PKT_SIZE+4]; |
136 |
|
137 |
/* If we don't have an input tag, we work temporarily as an access port */ |
138 |
if (!sp->input_tag) { |
139 |
ethsw_iv_access(t,sp,op); |
140 |
return; |
141 |
} |
142 |
|
143 |
switch(op->vlan_port_type) { |
144 |
/* 802.1Q -> Access: pop tag */ |
145 |
case ETHSW_PORT_TYPE_ACCESS: |
146 |
dot1q_pop_tag(pkt,sp); |
147 |
netio_send(op,pkt,sp->pkt_len-4); |
148 |
break; |
149 |
|
150 |
/* 802.1Q -> 802.1Q: pop tag if native VLAN in output otherwise no-op */ |
151 |
case ETHSW_PORT_TYPE_DOT1Q: |
152 |
if (op->vlan_id == sp->input_vlan) { |
153 |
dot1q_pop_tag(pkt,sp); |
154 |
netio_send(op,pkt,sp->pkt_len-4); |
155 |
} else { |
156 |
netio_send(op,sp->pkt,sp->pkt_len); |
157 |
} |
158 |
break; |
159 |
|
160 |
default: |
161 |
fprintf(stderr,"ethsw_iv_dot1q: unknown port type %u\n", |
162 |
op->vlan_port_type); |
163 |
} |
164 |
} |
165 |
|
166 |
/* Flood a packet */ |
167 |
static void ethsw_flood(ethsw_table_t *t,ethsw_packet_t *sp) |
168 |
{ |
169 |
ethsw_input_vector_t input_vector; |
170 |
netio_desc_t *op; |
171 |
int i; |
172 |
|
173 |
input_vector = sp->input_port->vlan_input_vector; |
174 |
assert(input_vector != NULL); |
175 |
|
176 |
for(i=0;i<ETHSW_MAX_NIO;i++) { |
177 |
op = t->nio[i]; |
178 |
|
179 |
if (!op || (op == sp->input_port)) |
180 |
continue; |
181 |
|
182 |
/* skip output port configured in access mode with a different vlan */ |
183 |
if ((op->vlan_port_type == ETHSW_PORT_TYPE_ACCESS) && |
184 |
(op->vlan_id != sp->input_vlan)) |
185 |
continue; |
186 |
|
187 |
/* send the packet on output port */ |
188 |
input_vector(t,sp,op); |
189 |
} |
190 |
} |
191 |
|
192 |
/* Forward a packet */ |
193 |
static void ethsw_forward(ethsw_table_t *t,ethsw_packet_t *sp) |
194 |
{ |
195 |
n_eth_hdr_t *hdr = (n_eth_hdr_t *)sp->pkt; |
196 |
ethsw_input_vector_t input_vector; |
197 |
ethsw_mac_entry_t *entry; |
198 |
u_int h_index; |
199 |
|
200 |
/* Learn the source MAC address */ |
201 |
h_index = ethsw_hash_index(&hdr->saddr,sp->input_vlan); |
202 |
entry = &t->mac_addr_table[h_index]; |
203 |
|
204 |
entry->nio = sp->input_port; |
205 |
entry->vlan_id = sp->input_vlan; |
206 |
entry->mac_addr = hdr->saddr; |
207 |
|
208 |
/* If we have a broadcast/multicast packet, flood it */ |
209 |
if (eth_addr_is_mcast(&hdr->daddr)) { |
210 |
ethsw_debug(t,"multicast dest, flooding packet.\n"); |
211 |
ethsw_flood(t,sp); |
212 |
return; |
213 |
} |
214 |
|
215 |
/* Lookup on the destination MAC address (unicast) */ |
216 |
h_index = ethsw_hash_index(&hdr->daddr,sp->input_vlan); |
217 |
entry = &t->mac_addr_table[h_index]; |
218 |
|
219 |
/* If the dest MAC is unknown, flood the packet */ |
220 |
if (memcmp(&entry->mac_addr,&hdr->daddr,N_ETH_ALEN) || |
221 |
(entry->vlan_id != sp->input_vlan)) |
222 |
{ |
223 |
ethsw_debug(t,"unknown dest, flooding packet.\n"); |
224 |
ethsw_flood(t,sp); |
225 |
return; |
226 |
} |
227 |
|
228 |
/* Forward the packet to the output port only */ |
229 |
if (entry->nio != sp->input_port) { |
230 |
input_vector = sp->input_port->vlan_input_vector; |
231 |
assert(input_vector != NULL); |
232 |
input_vector(t,sp,entry->nio); |
233 |
} else { |
234 |
ethsw_debug(t,"source and dest ports identical, dropping.\n"); |
235 |
} |
236 |
} |
237 |
|
238 |
/* Receive a packet and prepare its forwarding */ |
239 |
static inline int ethsw_receive(ethsw_table_t *t,netio_desc_t *nio, |
240 |
u_char *pkt,ssize_t pkt_len) |
241 |
{ |
242 |
n_eth_dot1q_hdr_t *dot1q_hdr; |
243 |
n_eth_isl_hdr_t *isl_hdr; |
244 |
n_eth_hdr_t *eth_hdr; |
245 |
n_eth_llc_hdr_t *llc_hdr; |
246 |
ethsw_packet_t sp; |
247 |
u_char *ptr; |
248 |
|
249 |
sp.input_port = nio; |
250 |
sp.input_vlan = 0; |
251 |
sp.pkt = pkt; |
252 |
sp.pkt_len = pkt_len; |
253 |
|
254 |
/* Skip runt packets */ |
255 |
if (sp.pkt_len < N_ETH_HLEN) |
256 |
return(-1); |
257 |
|
258 |
/* Determine the input VLAN */ |
259 |
switch(nio->vlan_port_type) { |
260 |
case ETHSW_PORT_TYPE_ACCESS: |
261 |
sp.input_vlan = nio->vlan_id; |
262 |
break; |
263 |
|
264 |
case ETHSW_PORT_TYPE_DOT1Q: |
265 |
dot1q_hdr = (n_eth_dot1q_hdr_t *)sp.pkt; |
266 |
|
267 |
/* use the native VLAN if no tag is found */ |
268 |
if (ntohs(dot1q_hdr->type) != N_ETH_PROTO_DOT1Q) { |
269 |
sp.input_vlan = nio->vlan_id; |
270 |
sp.input_tag = FALSE; |
271 |
} else { |
272 |
sp.input_vlan = ntohs(dot1q_hdr->vlan_id) & 0xFFF; |
273 |
sp.input_tag = TRUE; |
274 |
} |
275 |
break; |
276 |
|
277 |
case ETHSW_PORT_TYPE_ISL: |
278 |
/* Check that we have an ISL packet */ |
279 |
eth_hdr = (n_eth_hdr_t *)pkt; |
280 |
|
281 |
if (!eth_addr_is_cisco_isl(ð_hdr->daddr)) |
282 |
break; |
283 |
|
284 |
/* Verify LLC header */ |
285 |
llc_hdr = PTR_ADJUST(n_eth_llc_hdr_t *,eth_hdr,sizeof(n_eth_hdr_t)); |
286 |
if (!eth_llc_check_snap(llc_hdr)) |
287 |
break; |
288 |
|
289 |
/* Get the VLAN id */ |
290 |
isl_hdr = PTR_ADJUST(n_eth_isl_hdr_t *,llc_hdr, |
291 |
sizeof(n_eth_llc_hdr_t)); |
292 |
ptr = (u_char *)&isl_hdr->vlan; |
293 |
sp.input_vlan = (((u_int)ptr[0] << 8) | ptr[1]) >> 1; |
294 |
break; |
295 |
|
296 |
default: |
297 |
fprintf(stderr,"ethsw_receive: unknown port type %u\n", |
298 |
nio->vlan_port_type); |
299 |
return(-1); |
300 |
} |
301 |
|
302 |
if (sp.input_vlan != 0) |
303 |
ethsw_forward(t,&sp); |
304 |
return(0); |
305 |
} |
306 |
|
307 |
/* Receive a packet (handle the locking part) */ |
308 |
static int ethsw_recv_pkt(netio_desc_t *nio,u_char *pkt,ssize_t pkt_len, |
309 |
ethsw_table_t *t) |
310 |
{ |
311 |
ETHSW_LOCK(t); |
312 |
ethsw_receive(t,nio,pkt,pkt_len); |
313 |
ETHSW_UNLOCK(t); |
314 |
return(0); |
315 |
} |
316 |
|
317 |
/* Set a port as an access port with the specified VLAN */ |
318 |
static void set_access_port(netio_desc_t *nio,u_int vlan_id) |
319 |
{ |
320 |
nio->vlan_port_type = ETHSW_PORT_TYPE_ACCESS; |
321 |
nio->vlan_id = vlan_id; |
322 |
nio->vlan_input_vector = ethsw_iv_access; |
323 |
} |
324 |
|
325 |
/* Set a port as a 802.1Q trunk port */ |
326 |
static void set_dot1q_port(netio_desc_t *nio,u_int native_vlan) |
327 |
{ |
328 |
nio->vlan_port_type = ETHSW_PORT_TYPE_DOT1Q; |
329 |
nio->vlan_id = native_vlan; |
330 |
nio->vlan_input_vector = ethsw_iv_dot1q; |
331 |
} |
332 |
|
333 |
/* Acquire a reference to an Ethernet switch (increment reference count) */ |
334 |
ethsw_table_t *ethsw_acquire(char *name) |
335 |
{ |
336 |
return(registry_find(name,OBJ_TYPE_ETHSW)); |
337 |
} |
338 |
|
339 |
/* Release an Ethernet switch (decrement reference count) */ |
340 |
int ethsw_release(char *name) |
341 |
{ |
342 |
return(registry_unref(name,OBJ_TYPE_ETHSW)); |
343 |
} |
344 |
|
345 |
/* Create a virtual ethernet switch */ |
346 |
ethsw_table_t *ethsw_create(char *name) |
347 |
{ |
348 |
ethsw_table_t *t; |
349 |
|
350 |
/* Allocate a new switch structure */ |
351 |
if (!(t = malloc(sizeof(*t)))) |
352 |
return NULL; |
353 |
|
354 |
memset(t,0,sizeof(*t)); |
355 |
pthread_mutex_init(&t->lock,NULL); |
356 |
|
357 |
if (!(t->name = strdup(name))) |
358 |
goto err_name; |
359 |
|
360 |
/* Record this object in registry */ |
361 |
if (registry_add(t->name,OBJ_TYPE_ETHSW,t) == -1) { |
362 |
fprintf(stderr,"ethsw_create: unable to register switch '%s'\n",name); |
363 |
goto err_reg; |
364 |
} |
365 |
|
366 |
return t; |
367 |
|
368 |
err_reg: |
369 |
free(t->name); |
370 |
err_name: |
371 |
free(t); |
372 |
return NULL; |
373 |
} |
374 |
|
375 |
/* Add a NetIO descriptor to a virtual ethernet switch */ |
376 |
int ethsw_add_netio(ethsw_table_t *t,char *nio_name) |
377 |
{ |
378 |
netio_desc_t *nio; |
379 |
int i; |
380 |
|
381 |
ETHSW_LOCK(t); |
382 |
|
383 |
/* Try to find a free slot in the NIO array */ |
384 |
for(i=0;i<ETHSW_MAX_NIO;i++) |
385 |
if (t->nio[i] == NULL) |
386 |
break; |
387 |
|
388 |
/* No free slot found ... */ |
389 |
if (i == ETHSW_MAX_NIO) |
390 |
goto error; |
391 |
|
392 |
/* Acquire the NIO descriptor and increment its reference count */ |
393 |
if (!(nio = netio_acquire(nio_name))) |
394 |
goto error; |
395 |
|
396 |
/* By default, the port is an access port in VLAN 1 */ |
397 |
set_access_port(nio,1); |
398 |
|
399 |
t->nio[i] = nio; |
400 |
netio_rxl_add(nio,(netio_rx_handler_t)ethsw_recv_pkt,t,NULL); |
401 |
ETHSW_UNLOCK(t); |
402 |
return(0); |
403 |
|
404 |
error: |
405 |
ETHSW_UNLOCK(t); |
406 |
return(-1); |
407 |
} |
408 |
|
409 |
/* Free resources used by a NIO */ |
410 |
static void ethsw_free_nio(netio_desc_t *nio) |
411 |
{ |
412 |
netio_rxl_remove(nio); |
413 |
netio_release(nio->name); |
414 |
} |
415 |
|
416 |
/* Remove a NetIO descriptor from a virtual ethernet switch */ |
417 |
int ethsw_remove_netio(ethsw_table_t *t,char *nio_name) |
418 |
{ |
419 |
netio_desc_t *nio; |
420 |
int i; |
421 |
|
422 |
ETHSW_LOCK(t); |
423 |
|
424 |
if (!(nio = registry_exists(nio_name,OBJ_TYPE_NIO))) |
425 |
goto error; |
426 |
|
427 |
/* Try to find the NIO in the NIO array */ |
428 |
for(i=0;i<ETHSW_MAX_NIO;i++) |
429 |
if (t->nio[i] == nio) |
430 |
break; |
431 |
|
432 |
if (i == ETHSW_MAX_NIO) |
433 |
goto error; |
434 |
|
435 |
/* Invalidate this port in the MAC address table */ |
436 |
ethsw_invalidate_port(t,nio); |
437 |
t->nio[i] = NULL; |
438 |
|
439 |
ETHSW_UNLOCK(t); |
440 |
|
441 |
/* Remove the NIO from the RX multiplexer */ |
442 |
ethsw_free_nio(nio); |
443 |
return(0); |
444 |
|
445 |
error: |
446 |
ETHSW_UNLOCK(t); |
447 |
return(-1); |
448 |
} |
449 |
|
450 |
/* Clear the MAC address table */ |
451 |
int ethsw_clear_mac_addr_table(ethsw_table_t *t) |
452 |
{ |
453 |
ETHSW_LOCK(t); |
454 |
ethsw_invalidate(t); |
455 |
ETHSW_UNLOCK(t); |
456 |
return(0); |
457 |
} |
458 |
|
459 |
/* Iterate over all entries of the MAC address table */ |
460 |
int ethsw_iterate_mac_addr_table(ethsw_table_t *t,ethsw_foreach_entry_t cb, |
461 |
void *opt_arg) |
462 |
{ |
463 |
ethsw_mac_entry_t *entry; |
464 |
int i; |
465 |
|
466 |
ETHSW_LOCK(t); |
467 |
|
468 |
for(i=0;i<ETHSW_HASH_SIZE;i++) { |
469 |
entry = &t->mac_addr_table[i]; |
470 |
|
471 |
if (!entry->nio) |
472 |
continue; |
473 |
|
474 |
cb(t,entry,opt_arg); |
475 |
} |
476 |
|
477 |
ETHSW_UNLOCK(t); |
478 |
return(0); |
479 |
} |
480 |
|
481 |
/* Set port as an access port */ |
482 |
int ethsw_set_access_port(ethsw_table_t *t,char *nio_name,u_int vlan_id) |
483 |
{ |
484 |
int i,res = -1; |
485 |
|
486 |
ETHSW_LOCK(t); |
487 |
|
488 |
for(i=0;i<ETHSW_MAX_NIO;i++) |
489 |
if (t->nio[i] && !strcmp(t->nio[i]->name,nio_name)) { |
490 |
set_access_port(t->nio[i],vlan_id); |
491 |
res = 0; |
492 |
break; |
493 |
} |
494 |
|
495 |
ETHSW_UNLOCK(t); |
496 |
return(res); |
497 |
} |
498 |
|
499 |
/* Set port as a 802.1q trunk port */ |
500 |
int ethsw_set_dot1q_port(ethsw_table_t *t,char *nio_name,u_int native_vlan) |
501 |
{ |
502 |
int i,res = -1; |
503 |
|
504 |
ETHSW_LOCK(t); |
505 |
|
506 |
for(i=0;i<ETHSW_MAX_NIO;i++) |
507 |
if (t->nio[i] && !strcmp(t->nio[i]->name,nio_name)) { |
508 |
set_dot1q_port(t->nio[i],native_vlan); |
509 |
res = 0; |
510 |
break; |
511 |
} |
512 |
|
513 |
ETHSW_UNLOCK(t); |
514 |
return(res); |
515 |
} |
516 |
|
517 |
/* Save the configuration of a switch */ |
518 |
void ethsw_save_config(ethsw_table_t *t,FILE *fd) |
519 |
{ |
520 |
netio_desc_t *nio; |
521 |
int i; |
522 |
|
523 |
fprintf(fd,"ethsw create %s\n",t->name); |
524 |
|
525 |
ETHSW_LOCK(t); |
526 |
|
527 |
for(i=0;i<ETHSW_MAX_NIO;i++) |
528 |
{ |
529 |
nio = t->nio[i]; |
530 |
|
531 |
fprintf(fd,"ethsw add_nio %s %s\n",t->name,nio->name); |
532 |
|
533 |
switch(nio->vlan_port_type) { |
534 |
case ETHSW_PORT_TYPE_ACCESS: |
535 |
fprintf(fd,"ethsw set_access_port %s %s %u\n", |
536 |
t->name,nio->name,nio->vlan_id); |
537 |
break; |
538 |
|
539 |
case ETHSW_PORT_TYPE_DOT1Q: |
540 |
fprintf(fd,"ethsw set_dot1q_port %s %s %u\n", |
541 |
t->name,nio->name,nio->vlan_id); |
542 |
break; |
543 |
|
544 |
default: |
545 |
fprintf(stderr,"ethsw_save_config: unknown port type %u\n", |
546 |
nio->vlan_port_type); |
547 |
} |
548 |
} |
549 |
|
550 |
ETHSW_UNLOCK(t); |
551 |
|
552 |
fprintf(fd,"\n"); |
553 |
} |
554 |
|
555 |
/* Save configurations of all Ethernet switches */ |
556 |
static void ethsw_reg_save_config(registry_entry_t *entry,void *opt,int *err) |
557 |
{ |
558 |
ethsw_save_config((ethsw_table_t *)entry->data,(FILE *)opt); |
559 |
} |
560 |
|
561 |
void ethsw_save_config_all(FILE *fd) |
562 |
{ |
563 |
registry_foreach_type(OBJ_TYPE_ETHSW,ethsw_reg_save_config,fd,NULL); |
564 |
} |
565 |
|
566 |
/* Free resources used by a virtual ethernet switch */ |
567 |
static int ethsw_free(void *data,void *arg) |
568 |
{ |
569 |
ethsw_table_t *t = data; |
570 |
int i; |
571 |
|
572 |
for(i=0;i<ETHSW_MAX_NIO;i++) { |
573 |
if (!t->nio[i]) |
574 |
continue; |
575 |
|
576 |
ethsw_free_nio(t->nio[i]); |
577 |
} |
578 |
|
579 |
free(t->name); |
580 |
free(t); |
581 |
return(TRUE); |
582 |
} |
583 |
|
584 |
/* Delete a virtual ethernet switch */ |
585 |
int ethsw_delete(char *name) |
586 |
{ |
587 |
return(registry_delete_if_unused(name,OBJ_TYPE_ETHSW,ethsw_free,NULL)); |
588 |
} |
589 |
|
590 |
/* Delete all virtual ethernet switches */ |
591 |
int ethsw_delete_all(void) |
592 |
{ |
593 |
return(registry_delete_type(OBJ_TYPE_ETHSW,ethsw_free,NULL)); |
594 |
} |
595 |
|
596 |
/* Create a new interface */ |
597 |
static int ethsw_cfg_create_if(ethsw_table_t *t,char **tokens,int count) |
598 |
{ |
599 |
netio_desc_t *nio = NULL; |
600 |
int nio_type; |
601 |
|
602 |
nio_type = netio_get_type(tokens[2]); |
603 |
switch(nio_type) { |
604 |
case NETIO_TYPE_UNIX: |
605 |
if (count != 5) { |
606 |
fprintf(stderr,"ETHSW: invalid number of arguments " |
607 |
"for UNIX NIO\n"); |
608 |
break; |
609 |
} |
610 |
|
611 |
nio = netio_desc_create_unix(tokens[1],tokens[3],tokens[4]); |
612 |
break; |
613 |
|
614 |
case NETIO_TYPE_TAP: |
615 |
if (count != 4) { |
616 |
fprintf(stderr,"ETHSW: invalid number of arguments " |
617 |
"for TAP NIO\n"); |
618 |
break; |
619 |
} |
620 |
|
621 |
nio = netio_desc_create_tap(tokens[1],tokens[3]); |
622 |
break; |
623 |
|
624 |
case NETIO_TYPE_UDP: |
625 |
if (count != 6) { |
626 |
fprintf(stderr,"ETHSW: invalid number of arguments " |
627 |
"for UDP NIO\n"); |
628 |
break; |
629 |
} |
630 |
|
631 |
nio = netio_desc_create_udp(tokens[1],atoi(tokens[3]), |
632 |
tokens[4],atoi(tokens[5])); |
633 |
break; |
634 |
|
635 |
case NETIO_TYPE_TCP_CLI: |
636 |
if (count != 5) { |
637 |
fprintf(stderr,"ETHSW: invalid number of arguments " |
638 |
"for TCP CLI NIO\n"); |
639 |
break; |
640 |
} |
641 |
|
642 |
nio = netio_desc_create_tcp_cli(tokens[1],tokens[3],tokens[4]); |
643 |
break; |
644 |
|
645 |
case NETIO_TYPE_TCP_SER: |
646 |
if (count != 4) { |
647 |
fprintf(stderr,"ETHSW: invalid number of arguments " |
648 |
"for TCP SER NIO\n"); |
649 |
break; |
650 |
} |
651 |
|
652 |
nio = netio_desc_create_tcp_ser(tokens[1],tokens[3]); |
653 |
break; |
654 |
|
655 |
#ifdef GEN_ETH |
656 |
case NETIO_TYPE_GEN_ETH: |
657 |
if (count != 4) { |
658 |
fprintf(stderr,"ETHSW: invalid number of arguments " |
659 |
"for Generic Ethernet NIO\n"); |
660 |
break; |
661 |
} |
662 |
|
663 |
nio = netio_desc_create_geneth(tokens[1],tokens[3]); |
664 |
break; |
665 |
#endif |
666 |
|
667 |
#ifdef LINUX_ETH |
668 |
case NETIO_TYPE_LINUX_ETH: |
669 |
if (count != 4) { |
670 |
fprintf(stderr,"ETHSW: invalid number of arguments " |
671 |
"for Linux Ethernet NIO\n"); |
672 |
break; |
673 |
} |
674 |
|
675 |
nio = netio_desc_create_lnxeth(tokens[1],tokens[3]); |
676 |
break; |
677 |
#endif |
678 |
|
679 |
default: |
680 |
fprintf(stderr,"ETHSW: unknown/invalid NETIO type '%s'\n", |
681 |
tokens[2]); |
682 |
} |
683 |
|
684 |
if (!nio) { |
685 |
fprintf(stderr,"ETHSW: unable to create NETIO descriptor\n"); |
686 |
return(-1); |
687 |
} |
688 |
|
689 |
if (ethsw_add_netio(t,tokens[1]) == -1) { |
690 |
fprintf(stderr,"ETHSW: unable to add NETIO descriptor.\n"); |
691 |
netio_release(nio->name); |
692 |
return(-1); |
693 |
} |
694 |
|
695 |
netio_release(nio->name); |
696 |
return(0); |
697 |
} |
698 |
|
699 |
/* Set a port as an access port */ |
700 |
static int ethsw_cfg_set_access_port(ethsw_table_t *t,char **tokens,int count) |
701 |
{ |
702 |
/* 3 parameters: "ACCESS", IF, VLAN */ |
703 |
if (count != 3) { |
704 |
fprintf(stderr,"ETHSW: invalid access port description.\n"); |
705 |
return(-1); |
706 |
} |
707 |
|
708 |
return(ethsw_set_access_port(t,tokens[1],atoi(tokens[2]))); |
709 |
} |
710 |
|
711 |
/* Set a port as a 802.1q trunk port */ |
712 |
static int ethsw_cfg_set_dot1q_port(ethsw_table_t *t,char **tokens,int count) |
713 |
{ |
714 |
/* 3 parameters: "DOT1Q", IF, Native VLAN */ |
715 |
if (count != 3) { |
716 |
fprintf(stderr,"ETHSW: invalid trunk port description.\n"); |
717 |
return(-1); |
718 |
} |
719 |
|
720 |
return(ethsw_set_dot1q_port(t,tokens[1],atoi(tokens[2]))); |
721 |
} |
722 |
|
723 |
#define ETHSW_MAX_TOKENS 16 |
724 |
|
725 |
/* Handle a ETHSW configuration line */ |
726 |
static int ethsw_handle_cfg_line(ethsw_table_t *t,char *str) |
727 |
{ |
728 |
char *tokens[ETHSW_MAX_TOKENS]; |
729 |
int count; |
730 |
|
731 |
if ((count = m_strsplit(str,':',tokens,ETHSW_MAX_TOKENS)) <= 1) |
732 |
return(-1); |
733 |
|
734 |
if (!strcmp(tokens[0],"IF")) |
735 |
return(ethsw_cfg_create_if(t,tokens,count)); |
736 |
else if (!strcmp(tokens[0],"ACCESS")) |
737 |
return(ethsw_cfg_set_access_port(t,tokens,count)); |
738 |
else if (!strcmp(tokens[0],"DOT1Q")) |
739 |
return(ethsw_cfg_set_dot1q_port(t,tokens,count)); |
740 |
|
741 |
fprintf(stderr, |
742 |
"ETHSW: Unknown statement \"%s\" (allowed: IF,ACCESS,TRUNK)\n", |
743 |
tokens[0]); |
744 |
return(-1); |
745 |
} |
746 |
|
747 |
/* Read a ETHSW configuration file */ |
748 |
static int ethsw_read_cfg_file(ethsw_table_t *t,char *filename) |
749 |
{ |
750 |
char buffer[1024],*ptr; |
751 |
FILE *fd; |
752 |
|
753 |
if (!(fd = fopen(filename,"r"))) { |
754 |
perror("fopen"); |
755 |
return(-1); |
756 |
} |
757 |
|
758 |
while(!feof(fd)) { |
759 |
if (!fgets(buffer,sizeof(buffer),fd)) |
760 |
break; |
761 |
|
762 |
/* skip comments and end of line */ |
763 |
if ((ptr = strpbrk(buffer,"#\r\n")) != NULL) |
764 |
*ptr = 0; |
765 |
|
766 |
/* analyze non-empty lines */ |
767 |
if (strchr(buffer,':')) |
768 |
ethsw_handle_cfg_line(t,buffer); |
769 |
} |
770 |
|
771 |
fclose(fd); |
772 |
return(0); |
773 |
} |
774 |
|
775 |
/* Start a virtual Ethernet switch */ |
776 |
int ethsw_start(char *filename) |
777 |
{ |
778 |
ethsw_table_t *t; |
779 |
|
780 |
if (!(t = ethsw_create("default"))) { |
781 |
fprintf(stderr,"ETHSW: unable to create virtual fabric table.\n"); |
782 |
return(-1); |
783 |
} |
784 |
|
785 |
if (ethsw_read_cfg_file(t,filename) == -1) { |
786 |
fprintf(stderr,"ETHSW: unable to parse configuration file.\n"); |
787 |
return(-1); |
788 |
} |
789 |
|
790 |
ethsw_release("default"); |
791 |
return(0); |
792 |
} |