1 |
/* |
2 |
* Cisco router simulation platform. |
3 |
* Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) |
4 |
* |
5 |
* ATM utility functions and Virtual ATM switch. |
6 |
* |
7 |
* HEC and AAL5 CRC computation functions are from Charles Michael Heard |
8 |
* and can be found at (no licence specified, this is to check!): |
9 |
* |
10 |
* http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/ |
11 |
*/ |
12 |
|
13 |
#include <stdio.h> |
14 |
#include <stdlib.h> |
15 |
#include <string.h> |
16 |
#include <unistd.h> |
17 |
#include <pthread.h> |
18 |
#include <errno.h> |
19 |
#include <sys/select.h> |
20 |
#include <sys/time.h> |
21 |
#include <sys/types.h> |
22 |
|
23 |
|
24 |
#include "utils.h" |
25 |
#include "registry.h" |
26 |
#include "atm.h" |
27 |
#include "net_io.h" |
28 |
|
29 |
/********************************************************************/ |
30 |
#define HEC_GENERATOR 0x107 /* x^8 + x^2 + x + 1 */ |
31 |
#define COSET_LEADER 0x055 /* x^6 + x^4 + x^2 + 1 */ |
32 |
|
33 |
m_uint8_t hec_syndrome_table[256]; |
34 |
|
35 |
/* Generate a table of CRC-8 syndromes for all possible input bytes */ |
36 |
static void gen_syndrome_table(void) |
37 |
{ |
38 |
int i,j,syndrome; |
39 |
|
40 |
for(i=0;i<256;i++) { |
41 |
syndrome = i; |
42 |
|
43 |
for(j=0;j<8;j++) { |
44 |
if (syndrome & 0x80) |
45 |
syndrome = (syndrome << 1) ^ HEC_GENERATOR; |
46 |
else |
47 |
syndrome = (syndrome << 1); |
48 |
} |
49 |
hec_syndrome_table[i] = (unsigned char)syndrome; |
50 |
} |
51 |
} |
52 |
|
53 |
/* Compute HEC field for ATM header */ |
54 |
m_uint8_t atm_compute_hec(m_uint8_t *cell_header) |
55 |
{ |
56 |
register m_uint8_t hec_accum = 0; |
57 |
register int i; |
58 |
|
59 |
/* |
60 |
* calculate CRC-8 remainder over first four bytes of cell header. |
61 |
* exclusive-or with coset leader & insert into fifth header byte. |
62 |
*/ |
63 |
for(i=0;i<4;i++) |
64 |
hec_accum = hec_syndrome_table[hec_accum ^ cell_header[i]]; |
65 |
|
66 |
return(hec_accum ^ COSET_LEADER); |
67 |
} |
68 |
|
69 |
/* Insert HEC field into an ATM header */ |
70 |
void atm_insert_hec(m_uint8_t *cell_header) |
71 |
{ |
72 |
cell_header[4] = atm_compute_hec(cell_header); |
73 |
} |
74 |
|
75 |
/* Initialize ATM code (for HEC checksums) */ |
76 |
void atm_init(void) |
77 |
{ |
78 |
gen_syndrome_table(); |
79 |
} |
80 |
|
81 |
/* VPC hash function */ |
82 |
static inline u_int atmsw_vpc_hash(u_int vpi) |
83 |
{ |
84 |
return((vpi ^ (vpi >> 8)) & (ATMSW_VP_HASH_SIZE-1)); |
85 |
} |
86 |
|
87 |
/* VCC hash function */ |
88 |
static inline u_int atmsw_vcc_hash(u_int vpi,u_int vci) |
89 |
{ |
90 |
return((vpi ^ vci) & (ATMSW_VC_HASH_SIZE-1)); |
91 |
} |
92 |
|
93 |
/* VP lookup */ |
94 |
atmsw_vp_conn_t *atmsw_vp_lookup(atmsw_table_t *t,netio_desc_t *input, |
95 |
u_int vpi) |
96 |
{ |
97 |
atmsw_vp_conn_t *swc; |
98 |
|
99 |
for(swc=t->vp_table[atmsw_vpc_hash(vpi)];swc;swc=swc->next) |
100 |
if ((swc->input == input) && (swc->vpi_in == vpi)) |
101 |
return swc; |
102 |
|
103 |
return NULL; |
104 |
} |
105 |
|
106 |
/* VC lookup */ |
107 |
atmsw_vc_conn_t *atmsw_vc_lookup(atmsw_table_t *t,netio_desc_t *input, |
108 |
u_int vpi,u_int vci) |
109 |
{ |
110 |
atmsw_vc_conn_t *swc; |
111 |
|
112 |
for(swc=t->vc_table[atmsw_vcc_hash(vpi,vci)];swc;swc=swc->next) |
113 |
if ((swc->input == input) && (swc->vpi_in == vpi) && |
114 |
(swc->vci_in == vci)) |
115 |
return swc; |
116 |
|
117 |
return NULL; |
118 |
} |
119 |
|
120 |
/* VP switching */ |
121 |
void atmsw_vp_switch(atmsw_vp_conn_t *vpc,m_uint8_t *cell) |
122 |
{ |
123 |
m_uint32_t atm_hdr; |
124 |
|
125 |
/* rewrite the atm header with new vpi */ |
126 |
atm_hdr = ntohl(*(m_uint32_t *)cell); |
127 |
atm_hdr = atm_hdr & ~ATM_HDR_VPI_MASK; |
128 |
atm_hdr |= vpc->vpi_out << ATM_HDR_VPI_SHIFT; |
129 |
*(m_uint32_t *)cell = htonl(atm_hdr); |
130 |
|
131 |
/* recompute HEC field */ |
132 |
atm_insert_hec(cell); |
133 |
|
134 |
/* update the statistics counter */ |
135 |
vpc->cell_cnt++; |
136 |
} |
137 |
|
138 |
/* VC switching */ |
139 |
void atmsw_vc_switch(atmsw_vc_conn_t *vcc,m_uint8_t *cell) |
140 |
{ |
141 |
m_uint32_t atm_hdr; |
142 |
|
143 |
/* rewrite the atm header with new vpi/vci */ |
144 |
atm_hdr = ntohl(*(m_uint32_t *)cell); |
145 |
|
146 |
atm_hdr = atm_hdr & ~(ATM_HDR_VPI_MASK|ATM_HDR_VCI_MASK); |
147 |
atm_hdr |= vcc->vpi_out << ATM_HDR_VPI_SHIFT; |
148 |
atm_hdr |= vcc->vci_out << ATM_HDR_VCI_SHIFT; |
149 |
*(m_uint32_t *)cell = htonl(atm_hdr); |
150 |
|
151 |
/* recompute HEC field */ |
152 |
atm_insert_hec(cell); |
153 |
|
154 |
/* update the statistics counter */ |
155 |
vcc->cell_cnt++; |
156 |
} |
157 |
|
158 |
/* Handle an ATM cell */ |
159 |
ssize_t atmsw_handle_cell(atmsw_table_t *t,netio_desc_t *input, |
160 |
m_uint8_t *cell) |
161 |
{ |
162 |
m_uint32_t atm_hdr,vpi,vci; |
163 |
netio_desc_t *output = NULL; |
164 |
atmsw_vp_conn_t *vpc; |
165 |
atmsw_vc_conn_t *vcc; |
166 |
ssize_t len; |
167 |
|
168 |
/* Extract VPI/VCI information */ |
169 |
atm_hdr = ntohl(*(m_uint32_t *)cell); |
170 |
|
171 |
vpi = (atm_hdr & ATM_HDR_VPI_MASK) >> ATM_HDR_VPI_SHIFT; |
172 |
vci = (atm_hdr & ATM_HDR_VCI_MASK) >> ATM_HDR_VCI_SHIFT; |
173 |
|
174 |
/* VP switching */ |
175 |
if ((vpc = atmsw_vp_lookup(t,input,vpi)) != NULL) { |
176 |
atmsw_vp_switch(vpc,cell); |
177 |
output = vpc->output; |
178 |
} else { |
179 |
/* VC switching */ |
180 |
if ((vcc = atmsw_vc_lookup(t,input,vpi,vci)) != NULL) { |
181 |
atmsw_vc_switch(vcc,cell); |
182 |
output = vcc->output; |
183 |
} |
184 |
} |
185 |
|
186 |
len = netio_send(output,cell,ATM_CELL_SIZE); |
187 |
|
188 |
if (len != ATM_CELL_SIZE) { |
189 |
t->cell_drop++; |
190 |
return(-1); |
191 |
} |
192 |
|
193 |
return(0); |
194 |
} |
195 |
|
196 |
/* Receive an ATM cell */ |
197 |
static int atmsw_recv_cell(netio_desc_t *nio,u_char *atm_cell,ssize_t cell_len, |
198 |
atmsw_table_t *t) |
199 |
{ |
200 |
int res; |
201 |
|
202 |
if (cell_len != ATM_CELL_SIZE) |
203 |
return(-1); |
204 |
|
205 |
ATMSW_LOCK(t); |
206 |
res = atmsw_handle_cell(t,nio,atm_cell); |
207 |
ATMSW_UNLOCK(t); |
208 |
return(res); |
209 |
} |
210 |
|
211 |
/* Acquire a reference to an ATM switch (increment reference count) */ |
212 |
atmsw_table_t *atmsw_acquire(char *name) |
213 |
{ |
214 |
return(registry_find(name,OBJ_TYPE_ATMSW)); |
215 |
} |
216 |
|
217 |
/* Release an ATM switch (decrement reference count) */ |
218 |
int atmsw_release(char *name) |
219 |
{ |
220 |
return(registry_unref(name,OBJ_TYPE_ATMSW)); |
221 |
} |
222 |
|
223 |
/* Create a virtual switch table */ |
224 |
atmsw_table_t *atmsw_create_table(char *name) |
225 |
{ |
226 |
atmsw_table_t *t; |
227 |
|
228 |
/* Allocate a new switch structure */ |
229 |
if (!(t = malloc(sizeof(*t)))) |
230 |
return NULL; |
231 |
|
232 |
memset(t,0,sizeof(*t)); |
233 |
pthread_mutex_init(&t->lock,NULL); |
234 |
mp_create_fixed_pool(&t->mp,"ATM Switch"); |
235 |
|
236 |
if (!(t->name = mp_strdup(&t->mp,name))) |
237 |
goto err_name; |
238 |
|
239 |
/* Record this object in registry */ |
240 |
if (registry_add(t->name,OBJ_TYPE_ATMSW,t) == -1) { |
241 |
fprintf(stderr,"atmsw_create_table: unable to create switch '%s'\n", |
242 |
name); |
243 |
goto err_reg; |
244 |
} |
245 |
|
246 |
return t; |
247 |
|
248 |
err_reg: |
249 |
err_name: |
250 |
mp_free_pool(&t->mp); |
251 |
free(t); |
252 |
return NULL; |
253 |
} |
254 |
|
255 |
/* Free resources used by a VPC */ |
256 |
static void atmsw_release_vpc(atmsw_vp_conn_t *swc) |
257 |
{ |
258 |
if (swc) { |
259 |
/* release input NIO */ |
260 |
if (swc->input) { |
261 |
netio_rxl_remove(swc->input); |
262 |
netio_release(swc->input->name); |
263 |
} |
264 |
|
265 |
/* release output NIO */ |
266 |
if (swc->output) |
267 |
netio_release(swc->output->name); |
268 |
} |
269 |
} |
270 |
|
271 |
/* Free resources used by a VCC */ |
272 |
static void atmsw_release_vcc(atmsw_vc_conn_t *swc) |
273 |
{ |
274 |
if (swc) { |
275 |
/* release input NIO */ |
276 |
if (swc->input) { |
277 |
netio_rxl_remove(swc->input); |
278 |
netio_release(swc->input->name); |
279 |
} |
280 |
|
281 |
/* release output NIO */ |
282 |
if (swc->output) |
283 |
netio_release(swc->output->name); |
284 |
} |
285 |
} |
286 |
|
287 |
/* Create a VP switch connection */ |
288 |
int atmsw_create_vpc(atmsw_table_t *t,char *nio_input,u_int vpi_in, |
289 |
char *nio_output,u_int vpi_out) |
290 |
{ |
291 |
atmsw_vp_conn_t *swc; |
292 |
u_int hbucket; |
293 |
|
294 |
ATMSW_LOCK(t); |
295 |
|
296 |
/* Allocate a new switch connection */ |
297 |
if (!(swc = mp_alloc(&t->mp,sizeof(*swc)))) { |
298 |
ATMSW_UNLOCK(t); |
299 |
return(-1); |
300 |
} |
301 |
|
302 |
swc->input = netio_acquire(nio_input); |
303 |
swc->output = netio_acquire(nio_output); |
304 |
swc->vpi_in = vpi_in; |
305 |
swc->vpi_out = vpi_out; |
306 |
|
307 |
/* Check these NIOs are valid and the input VPI does not exists */ |
308 |
if (!swc->input || !swc->output || atmsw_vp_lookup(t,swc->input,vpi_in)) |
309 |
goto error; |
310 |
|
311 |
/* Add as a RX listener */ |
312 |
if (netio_rxl_add(swc->input,(netio_rx_handler_t)atmsw_recv_cell, |
313 |
t,NULL) == -1) |
314 |
goto error; |
315 |
|
316 |
hbucket = atmsw_vpc_hash(vpi_in); |
317 |
swc->next = t->vp_table[hbucket]; |
318 |
t->vp_table[hbucket] = swc; |
319 |
ATMSW_UNLOCK(t); |
320 |
return(0); |
321 |
|
322 |
error: |
323 |
ATMSW_UNLOCK(t); |
324 |
atmsw_release_vpc(swc); |
325 |
mp_free(swc); |
326 |
return(-1); |
327 |
} |
328 |
|
329 |
/* Delete a VP switch connection */ |
330 |
int atmsw_delete_vpc(atmsw_table_t *t,char *nio_input,u_int vpi_in, |
331 |
char *nio_output,u_int vpi_out) |
332 |
{ |
333 |
netio_desc_t *input,*output; |
334 |
atmsw_vp_conn_t **swc,*p; |
335 |
u_int hbucket; |
336 |
|
337 |
ATMSW_LOCK(t); |
338 |
|
339 |
input = registry_exists(nio_input,OBJ_TYPE_NIO); |
340 |
output = registry_exists(nio_output,OBJ_TYPE_NIO); |
341 |
|
342 |
if (!input || !output) { |
343 |
ATMSW_UNLOCK(t); |
344 |
return(-1); |
345 |
} |
346 |
|
347 |
hbucket = atmsw_vpc_hash(vpi_in); |
348 |
for(swc=&t->vp_table[hbucket];*swc;swc=&(*swc)->next) |
349 |
{ |
350 |
p = *swc; |
351 |
|
352 |
if ((p->input == input) && (p->output == output) && |
353 |
(p->vpi_in == vpi_in) && (p->vpi_out == vpi_out)) |
354 |
{ |
355 |
/* found a matching VP, remove it */ |
356 |
*swc = (*swc)->next; |
357 |
ATMSW_UNLOCK(t); |
358 |
|
359 |
atmsw_release_vpc(p); |
360 |
mp_free(p); |
361 |
return(0); |
362 |
} |
363 |
} |
364 |
|
365 |
ATMSW_UNLOCK(t); |
366 |
return(-1); |
367 |
} |
368 |
|
369 |
/* Create a VC switch connection */ |
370 |
int atmsw_create_vcc(atmsw_table_t *t, |
371 |
char *input,u_int vpi_in,u_int vci_in, |
372 |
char *output,u_int vpi_out,u_int vci_out) |
373 |
{ |
374 |
atmsw_vc_conn_t *swc; |
375 |
u_int hbucket; |
376 |
|
377 |
ATMSW_LOCK(t); |
378 |
|
379 |
/* Allocate a new switch connection */ |
380 |
if (!(swc = mp_alloc(&t->mp,sizeof(*swc)))) { |
381 |
ATMSW_UNLOCK(t); |
382 |
return(-1); |
383 |
} |
384 |
|
385 |
swc->input = netio_acquire(input); |
386 |
swc->output = netio_acquire(output); |
387 |
swc->vpi_in = vpi_in; |
388 |
swc->vci_in = vci_in; |
389 |
swc->vpi_out = vpi_out; |
390 |
swc->vci_out = vci_out; |
391 |
|
392 |
/* Ensure that there is not already VP switching */ |
393 |
if (atmsw_vp_lookup(t,swc->input,vpi_in) != NULL) { |
394 |
fprintf(stderr,"atmsw_create_vcc: VP switching already exists for " |
395 |
"VPI=%u\n",vpi_in); |
396 |
goto error; |
397 |
} |
398 |
|
399 |
/* Check these NIOs are valid and the input VPI does not exists */ |
400 |
if (!swc->input || !swc->output || |
401 |
atmsw_vc_lookup(t,swc->input,vpi_in,vci_in)) |
402 |
goto error; |
403 |
|
404 |
/* Add as a RX listener */ |
405 |
if (netio_rxl_add(swc->input,(netio_rx_handler_t)atmsw_recv_cell, |
406 |
t,NULL) == -1) |
407 |
goto error; |
408 |
|
409 |
hbucket = atmsw_vcc_hash(vpi_in,vci_in); |
410 |
swc->next = t->vc_table[hbucket]; |
411 |
t->vc_table[hbucket] = swc; |
412 |
ATMSW_UNLOCK(t); |
413 |
return(0); |
414 |
|
415 |
error: |
416 |
ATMSW_UNLOCK(t); |
417 |
atmsw_release_vcc(swc); |
418 |
mp_free(swc); |
419 |
return(-1); |
420 |
} |
421 |
|
422 |
/* Delete a VC switch connection */ |
423 |
int atmsw_delete_vcc(atmsw_table_t *t, |
424 |
char *nio_input,u_int vpi_in,u_int vci_in, |
425 |
char *nio_output,u_int vpi_out,u_int vci_out) |
426 |
{ |
427 |
netio_desc_t *input,*output; |
428 |
atmsw_vc_conn_t **swc,*p; |
429 |
u_int hbucket; |
430 |
|
431 |
ATMSW_LOCK(t); |
432 |
|
433 |
input = registry_exists(nio_input,OBJ_TYPE_NIO); |
434 |
output = registry_exists(nio_output,OBJ_TYPE_NIO); |
435 |
|
436 |
hbucket = atmsw_vcc_hash(vpi_in,vci_in); |
437 |
for(swc=&t->vc_table[hbucket];*swc;swc=&(*swc)->next) |
438 |
{ |
439 |
p = *swc; |
440 |
|
441 |
if ((p->input == input) && (p->output == output) && |
442 |
(p->vpi_in == vpi_in) && (p->vci_in == vci_in) && |
443 |
(p->vpi_out == vpi_out) && (p->vci_out == vci_out)) |
444 |
{ |
445 |
/* found a matching VP, remove it */ |
446 |
*swc = (*swc)->next; |
447 |
ATMSW_UNLOCK(t); |
448 |
|
449 |
atmsw_release_vcc(p); |
450 |
mp_free(p); |
451 |
return(0); |
452 |
} |
453 |
} |
454 |
|
455 |
ATMSW_UNLOCK(t); |
456 |
return(-1); |
457 |
} |
458 |
|
459 |
/* Free resources used by an ATM switch */ |
460 |
static int atmsw_free(void *data,void *arg) |
461 |
{ |
462 |
atmsw_table_t *t = data; |
463 |
atmsw_vp_conn_t *vp; |
464 |
atmsw_vc_conn_t *vc; |
465 |
int i; |
466 |
|
467 |
/* Remove all VPs */ |
468 |
for(i=0;i<ATMSW_VP_HASH_SIZE;i++) |
469 |
for(vp=t->vp_table[i];vp;vp=vp->next) |
470 |
atmsw_release_vpc(vp); |
471 |
|
472 |
/* Remove all VCs */ |
473 |
for(i=0;i<ATMSW_VC_HASH_SIZE;i++) |
474 |
for(vc=t->vc_table[i];vc;vc=vc->next) |
475 |
atmsw_release_vcc(vc); |
476 |
|
477 |
mp_free_pool(&t->mp); |
478 |
free(t); |
479 |
return(TRUE); |
480 |
} |
481 |
|
482 |
/* Delete an ATM switch */ |
483 |
int atmsw_delete(char *name) |
484 |
{ |
485 |
return(registry_delete_if_unused(name,OBJ_TYPE_ATMSW,atmsw_free,NULL)); |
486 |
} |
487 |
|
488 |
/* Delete all ATM switches */ |
489 |
int atmsw_delete_all(void) |
490 |
{ |
491 |
return(registry_delete_type(OBJ_TYPE_ATMSW,atmsw_free,NULL)); |
492 |
} |
493 |
|
494 |
/* Save the configuration of an ATM switch */ |
495 |
void atmsw_save_config(atmsw_table_t *t,FILE *fd) |
496 |
{ |
497 |
atmsw_vp_conn_t *vp; |
498 |
atmsw_vc_conn_t *vc; |
499 |
int i; |
500 |
|
501 |
fprintf(fd,"atmsw create %s\n",t->name); |
502 |
|
503 |
ATMSW_LOCK(t); |
504 |
|
505 |
for(i=0;i<ATMSW_VP_HASH_SIZE;i++) { |
506 |
for(vp=t->vp_table[i];vp;vp=vp->next) { |
507 |
fprintf(fd,"atmsw create_vpc %s %s %u %s %u\n", |
508 |
t->name,vp->input->name,vp->vpi_in, |
509 |
vp->output->name,vp->vpi_out); |
510 |
} |
511 |
} |
512 |
|
513 |
for(i=0;i<ATMSW_VC_HASH_SIZE;i++) { |
514 |
for(vc=t->vc_table[i];vc;vc=vc->next) { |
515 |
fprintf(fd,"atmsw create_vcc %s %s %u %u %s %u %u\n", |
516 |
t->name,vc->input->name,vc->vpi_in,vc->vci_in, |
517 |
vc->output->name,vc->vpi_out,vc->vci_out); |
518 |
} |
519 |
} |
520 |
|
521 |
ATMSW_UNLOCK(t); |
522 |
|
523 |
fprintf(fd,"\n"); |
524 |
} |
525 |
|
526 |
/* Save configurations of all ATM switches */ |
527 |
static void atmsw_reg_save_config(registry_entry_t *entry,void *opt,int *err) |
528 |
{ |
529 |
atmsw_save_config((atmsw_table_t *)entry->data,(FILE *)opt); |
530 |
} |
531 |
|
532 |
void atmsw_save_config_all(FILE *fd) |
533 |
{ |
534 |
registry_foreach_type(OBJ_TYPE_ATMSW,atmsw_reg_save_config,fd,NULL); |
535 |
} |
536 |
|
537 |
/* Create a new interface */ |
538 |
int atmsw_cfg_create_if(atmsw_table_t *t,char **tokens,int count) |
539 |
{ |
540 |
netio_desc_t *nio = NULL; |
541 |
int nio_type; |
542 |
|
543 |
/* at least: IF, interface name, NetIO type */ |
544 |
if (count < 3) { |
545 |
fprintf(stderr,"atmsw_cfg_create_if: invalid interface description\n"); |
546 |
return(-1); |
547 |
} |
548 |
|
549 |
nio_type = netio_get_type(tokens[2]); |
550 |
switch(nio_type) { |
551 |
case NETIO_TYPE_UNIX: |
552 |
if (count != 5) { |
553 |
fprintf(stderr,"ATMSW: invalid number of arguments " |
554 |
"for UNIX NIO '%s'\n",tokens[1]); |
555 |
break; |
556 |
} |
557 |
|
558 |
nio = netio_desc_create_unix(tokens[1],tokens[3],tokens[4]); |
559 |
break; |
560 |
|
561 |
case NETIO_TYPE_UDP: |
562 |
if (count != 6) { |
563 |
fprintf(stderr,"ATMSW: invalid number of arguments " |
564 |
"for UDP NIO '%s'\n",tokens[1]); |
565 |
break; |
566 |
} |
567 |
|
568 |
nio = netio_desc_create_udp(tokens[1],atoi(tokens[3]), |
569 |
tokens[4],atoi(tokens[5])); |
570 |
break; |
571 |
|
572 |
case NETIO_TYPE_TCP_CLI: |
573 |
if (count != 5) { |
574 |
fprintf(stderr,"ATMSW: invalid number of arguments " |
575 |
"for TCP CLI NIO '%s'\n",tokens[1]); |
576 |
break; |
577 |
} |
578 |
|
579 |
nio = netio_desc_create_tcp_cli(tokens[1],tokens[3],tokens[4]); |
580 |
break; |
581 |
|
582 |
case NETIO_TYPE_TCP_SER: |
583 |
if (count != 4) { |
584 |
fprintf(stderr,"ATMSW: invalid number of arguments " |
585 |
"for TCP SER NIO '%s'\n",tokens[1]); |
586 |
break; |
587 |
} |
588 |
|
589 |
nio = netio_desc_create_tcp_ser(tokens[1],tokens[3]); |
590 |
break; |
591 |
|
592 |
default: |
593 |
fprintf(stderr,"ATMSW: unknown/invalid NETIO type '%s'\n", |
594 |
tokens[2]); |
595 |
} |
596 |
|
597 |
if (!nio) { |
598 |
fprintf(stderr,"ATMSW: unable to create NETIO descriptor of " |
599 |
"interface %s\n",tokens[1]); |
600 |
return(-1); |
601 |
} |
602 |
|
603 |
netio_release(nio->name); |
604 |
return(0); |
605 |
} |
606 |
|
607 |
/* Create a new Virtual Path Connection */ |
608 |
int atmsw_cfg_create_vpc(atmsw_table_t *t,char **tokens,int count) |
609 |
{ |
610 |
/* 5 parameters: "VP", InputIF, InVPI, OutputIF, OutVPI */ |
611 |
if (count != 5) { |
612 |
fprintf(stderr,"ATMSW: invalid VPC descriptor.\n"); |
613 |
return(-1); |
614 |
} |
615 |
|
616 |
return(atmsw_create_vpc(t,tokens[1],atoi(tokens[2]), |
617 |
tokens[3],atoi(tokens[4]))); |
618 |
} |
619 |
|
620 |
/* Create a new Virtual Channel Connection */ |
621 |
int atmsw_cfg_create_vcc(atmsw_table_t *t,char **tokens,int count) |
622 |
{ |
623 |
/* 7 parameters: "VP", InputIF, InVPI/VCI, OutputIF, OutVPI/VCI */ |
624 |
if (count != 7) { |
625 |
fprintf(stderr,"ATMSW: invalid VCC descriptor.\n"); |
626 |
return(-1); |
627 |
} |
628 |
|
629 |
return(atmsw_create_vcc(t,tokens[1],atoi(tokens[2]),atoi(tokens[3]), |
630 |
tokens[4],atoi(tokens[5]),atoi(tokens[6]))); |
631 |
} |
632 |
|
633 |
#define ATMSW_MAX_TOKENS 16 |
634 |
|
635 |
/* Handle an ATMSW configuration line */ |
636 |
int atmsw_handle_cfg_line(atmsw_table_t *t,char *str) |
637 |
{ |
638 |
char *tokens[ATMSW_MAX_TOKENS]; |
639 |
int count; |
640 |
|
641 |
if ((count = m_strsplit(str,':',tokens,ATMSW_MAX_TOKENS)) <= 1) |
642 |
return(-1); |
643 |
|
644 |
if (!strcmp(tokens[0],"IF")) |
645 |
return(atmsw_cfg_create_if(t,tokens,count)); |
646 |
else if (!strcmp(tokens[0],"VP")) |
647 |
return(atmsw_cfg_create_vpc(t,tokens,count)); |
648 |
else if (!strcmp(tokens[0],"VC")) |
649 |
return(atmsw_cfg_create_vcc(t,tokens,count)); |
650 |
|
651 |
fprintf(stderr,"ATMSW: Unknown statement \"%s\" (allowed: IF,VP,VC)\n", |
652 |
tokens[0]); |
653 |
return(-1); |
654 |
} |
655 |
|
656 |
/* Read an ATMSW configuration file */ |
657 |
int atmsw_read_cfg_file(atmsw_table_t *t,char *filename) |
658 |
{ |
659 |
char buffer[1024],*ptr; |
660 |
FILE *fd; |
661 |
|
662 |
if (!(fd = fopen(filename,"r"))) { |
663 |
perror("fopen"); |
664 |
return(-1); |
665 |
} |
666 |
|
667 |
while(!feof(fd)) { |
668 |
if (!fgets(buffer,sizeof(buffer),fd)) |
669 |
break; |
670 |
|
671 |
/* skip comments and end of line */ |
672 |
if ((ptr = strpbrk(buffer,"#\r\n")) != NULL) |
673 |
*ptr = 0; |
674 |
|
675 |
/* analyze non-empty lines */ |
676 |
if (strchr(buffer,':')) |
677 |
atmsw_handle_cfg_line(t,buffer); |
678 |
} |
679 |
|
680 |
fclose(fd); |
681 |
return(0); |
682 |
} |
683 |
|
684 |
/* Start a virtual ATM switch */ |
685 |
int atmsw_start(char *filename) |
686 |
{ |
687 |
atmsw_table_t *t; |
688 |
|
689 |
if (!(t = atmsw_create_table("default"))) { |
690 |
fprintf(stderr,"ATMSW: unable to create virtual fabric table.\n"); |
691 |
return(-1); |
692 |
} |
693 |
|
694 |
if (atmsw_read_cfg_file(t,filename) == -1) { |
695 |
fprintf(stderr,"ATMSW: unable to parse configuration file.\n"); |
696 |
return(-1); |
697 |
} |
698 |
|
699 |
atmsw_release("default"); |
700 |
return(0); |
701 |
} |