481 lines
19 KiB
C
481 lines
19 KiB
C
/* main.c - Do what ever a LANE client does */
|
|
|
|
/*
|
|
* Marko Kiiskila carnil@cs.tut.fi
|
|
*
|
|
* Copyright (c) 1996
|
|
* Tampere University of Technology - Telecommunications Laboratory
|
|
* All rights reserved.
|
|
*
|
|
* Permission to use, copy, modify and distribute this
|
|
* software and its documentation is hereby granted,
|
|
* provided that both the copyright notice and this
|
|
* permission notice appear in all copies of the software,
|
|
* derivative works or modified versions, and any portions
|
|
* thereof, that both notices appear in supporting
|
|
* documentation, and that the use of this software is
|
|
* acknowledged in any publications resulting from using
|
|
* the software.
|
|
*
|
|
* TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
|
|
* CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
|
|
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
|
|
* SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
/* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
|
|
|
|
/* Global includes */
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
#include <errno.h>
|
|
|
|
#include <atm.h>
|
|
#include <atmd.h>
|
|
|
|
#include <linux/atmlec.h>
|
|
|
|
/* Local incs */
|
|
#include "join.h"
|
|
#include "lec.h"
|
|
#include "address.h"
|
|
#include "display.h"
|
|
#include "kernel.h"
|
|
|
|
#define COMPONENT "main.c"
|
|
|
|
static void main_loop(void);
|
|
static int reset = 0;
|
|
|
|
void sig_reset(int a)
|
|
{
|
|
reset = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
static void usage(const char *progname)
|
|
{
|
|
printf("Usage: %s [-c LECS_address | -s LES_address] [-e esi] [-n VLAN_name]"
|
|
" [-m mesg_mask] [-l listen_address | selector] [-i interface_number]"
|
|
" [-I physical_interface_number]"
|
|
" [-t 1516|1580|4544|9234|18190] [-1] [-2] [-p] [-F logfile]"
|
|
" [-f Fore specific name]\n", progname);
|
|
}
|
|
|
|
/*
|
|
* My First C function (TM), hessu@cs.tut.fi
|
|
*/
|
|
static int esi_convert(char *parsestring, unsigned char *mac_addr)
|
|
{
|
|
const char *hexchars = "abcdefABCDEF0123456789";
|
|
char hexnum [17+1], curr;
|
|
int i = 0, j = -1, hexindex = 0, tmp;
|
|
char *k, *string;
|
|
|
|
if (strchr(parsestring,'.') || /* do we have separators like */
|
|
strchr(parsestring,':')) { /* 00:20:22:23:04:05 */
|
|
k = parsestring;
|
|
for (i = 0; i < strlen(parsestring); i++) {
|
|
curr = *k;
|
|
if (curr == ':' || curr == '.') { /* separator ? */
|
|
if (i - j == 3) { /* there were 2 hex characters */
|
|
;
|
|
}
|
|
else if (i - j == 2) { /* there was only 1 hex char */
|
|
hexnum [hexindex] = hexnum [hexindex-1];
|
|
hexnum [hexindex-1] = '0';
|
|
hexindex +=1;
|
|
}
|
|
else /* too many hexchars in a byte */
|
|
return -1;
|
|
j = i; /* j is the location of the last separator */
|
|
}
|
|
else if (strchr(hexchars, curr) == NULL) /* not a hexchar ? */
|
|
return -1;
|
|
else { /* we have a hex character */
|
|
hexnum [hexindex] = curr;
|
|
hexindex +=1;
|
|
}
|
|
k++;
|
|
}
|
|
hexnum [hexindex] = '\0';
|
|
string = hexnum;
|
|
} else { /* no separators */
|
|
k = parsestring;
|
|
while (*k != '\0') {
|
|
if (strchr(hexchars, *k) == NULL)
|
|
return -1;
|
|
k++;
|
|
}
|
|
|
|
string = parsestring;
|
|
}
|
|
|
|
/* the esi now looks like 002022230405 */
|
|
i = strlen(string);
|
|
if (i != 12)
|
|
return -1;
|
|
for(i=0; i<6; i++) {
|
|
sscanf(&string[i*2], "%2x", &tmp);
|
|
mac_addr[i] = (unsigned char)tmp;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Tells kernel what our LEC_ID is.
|
|
* Returns < 0 for serisous error
|
|
*/
|
|
static int set_lec_id(uint16_t lec_id)
|
|
{
|
|
struct atmlec_msg msg;
|
|
|
|
memset(&msg, 0, sizeof(struct atmlec_msg));
|
|
msg.type = l_set_lecid;
|
|
msg.content.normal.flag = lec_id;
|
|
if (msg_to_kernel(&msg, sizeof(struct atmlec_msg)) < 0) {
|
|
diag(COMPONENT, DIAG_ERROR, "Could not tell kernel LEC_ID\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Tell kernel the parameters this ELAN has.
|
|
* Returns < 0 for serious error
|
|
*/
|
|
static int config_kernel(void)
|
|
{
|
|
struct atmlec_msg msg;
|
|
|
|
memset(&msg, 0, sizeof(struct atmlec_msg));
|
|
msg.type = l_config;
|
|
msg.content.config.maximum_unknown_frame_count = lec_params.c10_max_unknown_frames;
|
|
msg.content.config.max_unknown_frame_time = lec_params.c11_max_unknown_frame_time;
|
|
msg.content.config.max_retry_count = lec_params.c13_max_retry_count;
|
|
msg.content.config.aging_time = lec_params.c17_aging_time;
|
|
msg.content.config.forward_delay_time = lec_params.c18_forward_delay_time;
|
|
msg.content.config.arp_response_time = lec_params.c20_le_arp_response_time;
|
|
msg.content.config.flush_timeout = lec_params.c21_flush_timeout;
|
|
msg.content.config.path_switching_delay = lec_params.c22_path_switching_delay;
|
|
msg.content.config.lane_version = (lec_params.c29_v2_capable) ? 2 : 1;
|
|
msg.content.config.mtu = maxmtu2itfmtu(lec_params.c3_max_frame_size);
|
|
msg.content.config.is_proxy = lec_params.c4_proxy_flag;
|
|
|
|
if (msg_to_kernel(&msg, sizeof(struct atmlec_msg)) < 0) {
|
|
diag(COMPONENT, DIAG_ERROR, "Could not tell kernel ELAN parameters\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
char mac_addr[ETH_ALEN];
|
|
char elan_name[32 + 1];
|
|
char preferred_les[ATM_ESA_LEN]; /* LANE2 */
|
|
char foreId[255]; /* Max size for a TLV */
|
|
char atm2textbuff[100];
|
|
char esibuff[20];
|
|
int esi_set = 0;
|
|
int listen_addr_set = 0;
|
|
int atm_set=0;
|
|
int proxy_flag = 0;
|
|
int lane_version = 0; /* LANE2 */
|
|
int max_frame_size = MTU_UNSPEC;
|
|
int lecs_method = LECS_WELLKNOWN;
|
|
int poll_ret = 0, itf = 0, phys_itf = 0, selector = 0;
|
|
struct sockaddr_atmsvc manual_atm_addr;
|
|
struct sockaddr_atmsvc listen_addr;
|
|
|
|
memset(elan_name, '\0', sizeof(elan_name));
|
|
memset(foreId, '\0', sizeof(foreId));
|
|
memset(preferred_les, 0, ATM_ESA_LEN);
|
|
memset(&manual_atm_addr, 0, sizeof(struct sockaddr_atmsvc));
|
|
memset(&listen_addr, 0, sizeof(struct sockaddr_atmsvc));
|
|
listen_addr.sas_family = AF_ATMSVC;
|
|
|
|
set_application("zeppelin"); /* for debug msgs */
|
|
|
|
while(poll_ret != -1) {
|
|
poll_ret = getopt(argc, argv, "c:e:n:s:m:l:i:I:q:12pf:t:F:");
|
|
switch(poll_ret) {
|
|
case 'c':
|
|
if (atm_set) {
|
|
usage(argv[0]);
|
|
exit(-1);
|
|
}
|
|
if (text2atm(optarg, (struct sockaddr *)&manual_atm_addr,
|
|
sizeof(struct sockaddr_atmsvc), T2A_NAME) < 0) {
|
|
diag(COMPONENT, DIAG_ERROR, "Invalid LECS address\n");
|
|
usage(argv[0]);
|
|
exit(-1);
|
|
}
|
|
atm2text(atm2textbuff, sizeof(atm2textbuff),
|
|
(struct sockaddr *)&manual_atm_addr, 0);
|
|
diag(COMPONENT, DIAG_INFO, "LECS address: %s\n", atm2textbuff);
|
|
lecs_method = LECS_MANUAL;
|
|
atm_set=1;
|
|
break;
|
|
case 'e':
|
|
if(esi_convert(optarg, mac_addr)<0) {
|
|
diag(COMPONENT, DIAG_ERROR, "Invalid ESI format\n");
|
|
usage(argv[0]);
|
|
exit(-1);
|
|
}
|
|
mac2text(esibuff, mac_addr);
|
|
diag(COMPONENT, DIAG_DEBUG, "LEC ESI:%s\n", esibuff);
|
|
esi_set=1;
|
|
break;
|
|
case 'n':
|
|
if (strlen(optarg) > 32) {
|
|
diag(COMPONENT, DIAG_ERROR, "ELAN name too long\n");
|
|
exit(-1);
|
|
}
|
|
strcpy(elan_name, optarg);
|
|
diag(COMPONENT, DIAG_INFO, "Vlan name :'%s'\n", elan_name);
|
|
break;
|
|
case 's':
|
|
if (atm_set) {
|
|
usage(argv[0]);
|
|
exit(-1);
|
|
}
|
|
if (text2atm(optarg, (struct sockaddr *)&manual_atm_addr,
|
|
sizeof(struct sockaddr_atmsvc), T2A_NAME) < 0) {
|
|
diag(COMPONENT, DIAG_ERROR, "Invalid LES address\n");
|
|
usage(argv[0]);
|
|
exit(-1);
|
|
}
|
|
atm2text(atm2textbuff, sizeof(atm2textbuff),
|
|
(struct sockaddr *)&manual_atm_addr, 0);
|
|
diag(COMPONENT, DIAG_INFO, "LES address: %s\n", atm2textbuff);
|
|
lecs_method = LECS_NONE;
|
|
atm_set=1;
|
|
break;
|
|
case 'm':
|
|
set_verbosity(NULL, DIAG_DEBUG);
|
|
break;
|
|
case 'l':
|
|
if (isdigit(optarg[0]) && strlen(optarg) < 4 &&
|
|
sscanf(optarg, "%d", &selector) &&
|
|
selector >=0 && selector <= 0xff) {
|
|
listen_addr.sas_addr.prv[ATM_ESA_LEN - 1] =
|
|
(char) selector;
|
|
diag(COMPONENT, DIAG_INFO, "Selector byte set "
|
|
"to %d", selector);
|
|
} else {
|
|
if (text2atm(optarg, (struct sockaddr *)&listen_addr,
|
|
sizeof(struct sockaddr_atmsvc), T2A_NAME) < 0) {
|
|
diag(COMPONENT, DIAG_ERROR, "Invalid ATM listen address\n");
|
|
usage(argv[0]);
|
|
exit(-1);
|
|
}
|
|
listen_addr_set = 1;
|
|
}
|
|
break;
|
|
case 'i':
|
|
if (sscanf(optarg, "%d", &itf) <= 0 || itf >= MAX_LEC_ITF) {
|
|
diag(COMPONENT, DIAG_ERROR, "Invalid interface number\n");
|
|
usage(argv[0]);
|
|
exit(-1);
|
|
}
|
|
diag(COMPONENT, DIAG_INFO, "Interface number set to %d\n", itf);
|
|
break;
|
|
case 'I':
|
|
if (sscanf(optarg, "%d", &phys_itf) <= 0 || phys_itf < 0) {
|
|
diag(COMPONENT, DIAG_ERROR, "Invalid physical interface number\n");
|
|
usage(argv[0]);
|
|
exit(-1);
|
|
}
|
|
diag(COMPONENT, DIAG_INFO, "Physical interface number set to %d\n", phys_itf);
|
|
break;
|
|
case 'q':
|
|
#if 0
|
|
if (text2qos(optarg,NULL,0) < 0) {
|
|
diag(COMPONENT, DIAG_ERROR, "Invalid QOS specification\n");
|
|
usage(argv[0]);
|
|
exit(-1);
|
|
}
|
|
qos_spec = optarg;
|
|
#endif
|
|
diag(COMPONENT, DIAG_INFO, "-q is deprecated, ignoring it\n");
|
|
break;
|
|
case '1':
|
|
lane_version = 1;
|
|
break;
|
|
case '2':
|
|
lane_version = 2;
|
|
break;
|
|
case 'p':
|
|
proxy_flag = 1;
|
|
break;
|
|
case 'f':
|
|
if (strlen(optarg) > 255) {
|
|
diag(COMPONENT, DIAG_ERROR, "foreId too long\n");
|
|
exit(-1);
|
|
}
|
|
memcpy (foreId, optarg, strlen(optarg));
|
|
foreId[strlen(optarg)] = '\0';
|
|
diag(COMPONENT, DIAG_INFO, "foreId :'%s'\n", foreId);
|
|
break;
|
|
case 't': /* ERIC */
|
|
if( !strncmp( optarg, "1516", 4 )) max_frame_size = MTU_1516;
|
|
else if( !strncmp( optarg, "1580", 4 )) max_frame_size = MTU_1580;
|
|
else if( !strncmp( optarg, "4544", 4 )) max_frame_size = MTU_4544;
|
|
else if( !strncmp( optarg, "9234", 4 )) max_frame_size = MTU_9234;
|
|
else if( !strncmp( optarg, "18190", 5 )) max_frame_size = MTU_18190;
|
|
break;
|
|
case 'F':
|
|
set_logfile(optarg);
|
|
diag(COMPONENT, DIAG_DEBUG, "logfile set to %s\n", optarg);
|
|
break;
|
|
case -1:
|
|
break;
|
|
default:
|
|
usage(argv[0]);
|
|
exit(-1);
|
|
}
|
|
}
|
|
if (argc != optind) {
|
|
usage(argv[0]);
|
|
exit(1);
|
|
}
|
|
if (lane_version == 1 && max_frame_size == MTU_1580) {
|
|
diag(COMPONENT, DIAG_ERROR, "MTU 1580 not defined with LANEv1\n");
|
|
exit(-1);
|
|
}
|
|
|
|
/* Reserve signals */
|
|
signal(SIGHUP, sig_reset);
|
|
|
|
/* Loop here until the Sun gets cold */
|
|
while (1) {
|
|
if (!listen_addr_set) {
|
|
char sel = listen_addr.sas_addr.prv[ATM_ESA_LEN - 1];
|
|
if (get_listenaddr(listen_addr.sas_addr.prv, phys_itf) < 0) {
|
|
diag(COMPONENT, DIAG_FATAL, "Could not figure out my ATM address\n");
|
|
exit(-1);
|
|
}
|
|
listen_addr.sas_addr.prv[ATM_ESA_LEN - 1] = sel;
|
|
}
|
|
|
|
atm2text(atm2textbuff, sizeof(atm2textbuff),
|
|
(struct sockaddr *)&listen_addr, A2T_NAME | A2T_PRETTY | A2T_LOCAL);
|
|
diag(COMPONENT, DIAG_INFO, "Our ATM address: %s\n", atm2textbuff);
|
|
|
|
if (!esi_set) {
|
|
if(addr_getesi(mac_addr, phys_itf) < 0) {
|
|
diag(COMPONENT, DIAG_ERROR, "Can't get ESI from kernel!\n");
|
|
return -1;
|
|
}
|
|
mac2text(esibuff, mac_addr);
|
|
diag(COMPONENT, DIAG_DEBUG, "LEC ESI:%s\n", esibuff);
|
|
}
|
|
|
|
if ((itf = kernel_init(mac_addr, itf)) < 0 ) {
|
|
diag(COMPONENT, DIAG_FATAL, "Kernel interface creation failed, exiting...\n");
|
|
return -1;
|
|
}
|
|
|
|
diag(COMPONENT, DIAG_DEBUG, "initializing lec parameters\n");
|
|
init_lec_params(mac_addr, elan_name, listen_addr.sas_addr.prv,
|
|
itf, foreId, max_frame_size, proxy_flag, lane_version);
|
|
|
|
diag(COMPONENT, DIAG_DEBUG, "About to connect LECS\n");
|
|
if (lec_configure(lecs_method, &manual_atm_addr, &listen_addr) < 0) {
|
|
close_connections();
|
|
random_delay();
|
|
continue;
|
|
}
|
|
diag(COMPONENT, DIAG_DEBUG, "About to connect LES\n");
|
|
if (les_connect(lecs_method, &manual_atm_addr, &listen_addr) < 0) {
|
|
close_connections();
|
|
random_delay();
|
|
continue;
|
|
}
|
|
diag(COMPONENT, DIAG_DEBUG, "About to connect BUS\n");
|
|
if (bus_connect() < 0) {
|
|
close_connections();
|
|
random_delay();
|
|
continue;
|
|
}
|
|
diag(COMPONENT, DIAG_DEBUG, "About to create data direct listen socket\n");
|
|
if (create_data_listen() < 0) {
|
|
close_connections();
|
|
random_delay();
|
|
continue;
|
|
}
|
|
diag(COMPONENT, DIAG_DEBUG, "About to tell kernel our LEC_ID %d\n", lec_params.c14_lec_id);
|
|
if (set_lec_id(lec_params.c14_lec_id) < 0) {
|
|
close_connections();
|
|
continue;
|
|
}
|
|
diag(COMPONENT, DIAG_DEBUG, "About to tell kernel LEC parameters\n");
|
|
if (config_kernel() < 0) {
|
|
close_connections();
|
|
continue;
|
|
}
|
|
|
|
diag(COMPONENT, DIAG_DEBUG, "Joined ELAN '%s' successfully\n", lec_params.c5_elan_name);
|
|
|
|
main_loop();
|
|
diag(COMPONENT, DIAG_INFO, "Resetting...\n");
|
|
close_connections();
|
|
random_delay();
|
|
|
|
reset = 0;
|
|
}
|
|
|
|
return 0; /* not reached */
|
|
}
|
|
|
|
/* zeppelin loops here when it is in operational state. The check
|
|
* against reset variable is probably not needed since select() will
|
|
* return < 0 when a signal interrupts it.
|
|
*/
|
|
static void main_loop(void)
|
|
{
|
|
fd_set rfds, cfds;
|
|
int retval, ret1, ret2, ret3;
|
|
|
|
while(!reset) {
|
|
retval = ret1 = ret2 = ret3 = 0;
|
|
FD_ZERO(&rfds);
|
|
conn_get_fds(&rfds);
|
|
FD_ZERO(&cfds);
|
|
conn_get_connecting_fds(&cfds);
|
|
|
|
retval = select(FD_SETSIZE, &rfds, &cfds, NULL, NULL);
|
|
diag(COMPONENT, DIAG_DEBUG, "main_loop: select returned %d\n", retval);
|
|
if (retval < 0) {
|
|
diag(COMPONENT, DIAG_ERROR, "main_loop: select: %s\n", strerror(errno));
|
|
break; /* leave main_loop */
|
|
}
|
|
if (retval == 0) {
|
|
/* Timeout, funny, since we have no timers */
|
|
continue;
|
|
}
|
|
if (FD_ISSET(lec_params.kernel->fd, &rfds)) {
|
|
ret1 = msg_from_kernel();
|
|
FD_CLR(lec_params.kernel->fd, &rfds);
|
|
}
|
|
ret2 = complete_connections(&cfds);
|
|
ret3 = check_connections(&rfds);
|
|
|
|
if (ret1 < 0 || ret2 < 0 || ret3 < 0)
|
|
break; /* leave main_loop */
|
|
}
|
|
|
|
diag(COMPONENT, DIAG_DEBUG, "exiting main_loop\n");
|
|
|
|
return;
|
|
}
|