450 lines
9.6 KiB
C
450 lines
9.6 KiB
C
/* isp.c - Internal Signaling Protocol test generator */
|
|
|
|
/* Written 1997-2000 by Werner Almesberger, EPFL-ICA */
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <atm.h>
|
|
#include <atmd.h>
|
|
#include <linux/atmsvc.h>
|
|
|
|
#include "isp.h"
|
|
|
|
|
|
extern int yyparse(void);
|
|
|
|
int quiet = 0;
|
|
int verbose = 0;
|
|
VAR *variables = NULL;
|
|
|
|
static int sock;
|
|
|
|
|
|
void send_msg(const struct atmsvc_msg *msg)
|
|
{
|
|
int wrote;
|
|
|
|
wrote = write(sock,msg,sizeof(*msg));
|
|
if (wrote == sizeof(*msg)) return;
|
|
if (wrote < 0) perror("write");
|
|
else fprintf(stderr,"bad write: %d != %d\n",wrote,sizeof(*msg));
|
|
exit(1);
|
|
}
|
|
|
|
|
|
void recv_msg(struct atmsvc_msg *msg)
|
|
{
|
|
int got;
|
|
|
|
got = read(sock,msg,sizeof(*msg));
|
|
if (got == sizeof(*msg)) return;
|
|
if (got < 0) perror("read");
|
|
else fprintf(stderr,"bad read: %d != %d\n",got,sizeof(*msg));
|
|
exit(1);
|
|
}
|
|
|
|
|
|
static struct errno_table {
|
|
const char *name;
|
|
int value;
|
|
} table[] = {
|
|
#include "errnos.inc"
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
|
|
static const char *errno2str(int code)
|
|
{
|
|
static char buf[30]; /* probably large enough :) */
|
|
const struct errno_table *walk;
|
|
|
|
for (walk = table; walk->name; walk++) {
|
|
if (walk->value == code) return walk->name;
|
|
if (walk->value == -code) {
|
|
sprintf(buf,"-%s",walk->name);
|
|
return buf;
|
|
}
|
|
}
|
|
sprintf(buf,"%d (0x%x)",code,code);
|
|
return buf;
|
|
}
|
|
|
|
|
|
/* Synchronized with include/linux/atmsvc.h:enum atmsvc_msg_type */
|
|
|
|
static struct {
|
|
const char *name;
|
|
int fields;
|
|
} types[] = {
|
|
{ "<invalid>", 0 },
|
|
{ "bind", F_VCC | F_SVC | F_SAP },
|
|
{ "connect", F_VCC | F_PVC | F_LOCAL | F_QOS | F_SVC | F_SAP },
|
|
{ "accept", F_VCC | F_LISTEN_VCC },
|
|
{ "reject", F_VCC | F_LISTEN_VCC | F_REPLY },
|
|
{ "listen", F_VCC | F_QOS | F_SVC | F_SAP },
|
|
{ "okay", F_VCC | F_PVC | F_LOCAL | F_QOS | F_SVC | F_SAP },
|
|
{ "error", F_VCC | F_REPLY },
|
|
{ "indicate", F_LISTEN_VCC | F_PVC | F_LOCAL | F_QOS | F_SVC | F_SAP},
|
|
{ "close", F_VCC | F_REPLY },
|
|
{ "itf_notify", F_PVC },
|
|
{ "modify", F_VCC | F_REPLY | F_QOS },
|
|
{ "identify", F_VCC | F_LISTEN_VCC | F_PVC },
|
|
{ "terminate", 0 }};
|
|
|
|
#define MSG_TYPES (sizeof(types)/sizeof(*types))
|
|
|
|
|
|
void print_value(VALUE val)
|
|
{
|
|
char buf[1000]; /* bigger than any MAX_ATM_*_LEN */
|
|
|
|
switch (val.type) {
|
|
case vt_text:
|
|
printf("\"%s\"",val.u.text);
|
|
return;
|
|
case vt_vcc:
|
|
printf("%s",kptr_print(&val.u.id));
|
|
return;
|
|
case vt_error:
|
|
printf("%s",errno2str(val.u.num));
|
|
return;
|
|
case vt_pvc:
|
|
if (atm2text(buf,sizeof(buf),(struct sockaddr *) &val.u.pvc,
|
|
A2T_PRETTY | A2T_NAME) < 0) strcpy(buf,"<invalid>");
|
|
printf("%s",buf);
|
|
return;
|
|
case vt_svc:
|
|
if (atm2text(buf,sizeof(buf),(struct sockaddr *) &val.u.svc,
|
|
A2T_PRETTY | A2T_NAME) < 0) strcpy(buf,"<invalid>");
|
|
printf("%s",buf);
|
|
return;
|
|
case vt_qos:
|
|
if (qos2text(buf,sizeof(buf),&val.u.qos,0) < 0)
|
|
strcpy(buf,"<invalid>");
|
|
printf("%s",buf);
|
|
return;
|
|
case vt_sap:
|
|
if (sap2text(buf,sizeof(buf),&val.u.sap,S2T_NAME) < 0)
|
|
strcpy(buf,"<invalid>");
|
|
printf("%s",buf);
|
|
return;
|
|
default:
|
|
fprintf(stderr,"\ninvalid value type %d\n",val.type);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
|
|
#define FIELD(FLD,MSG) \
|
|
if (fields & FLD) { \
|
|
printf("%s",MSG); \
|
|
print_value(pick(msg,FLD)); \
|
|
putchar('\n'); \
|
|
}
|
|
|
|
|
|
void dump_msg(const char *prefix,const struct atmsvc_msg *msg)
|
|
{
|
|
int fields;
|
|
|
|
if (msg->type >= MSG_TYPES) {
|
|
printf("%s: unknown message type %d\n",prefix,msg->type);
|
|
return;
|
|
}
|
|
fields = types[msg->type].fields;
|
|
printf("%s: %s\n",prefix,types[msg->type].name);
|
|
FIELD(F_VCC, " vcc = ");
|
|
FIELD(F_LISTEN_VCC, " listen_vcc = ");
|
|
FIELD(F_REPLY, " reply = ");
|
|
FIELD(F_PVC, " pvc = ");
|
|
FIELD(F_LOCAL, " local = ");
|
|
FIELD(F_QOS, " qos = ");
|
|
FIELD(F_SVC, " svc = ");
|
|
FIELD(F_SAP, " sap = ");
|
|
}
|
|
|
|
|
|
#undef FIELD
|
|
|
|
|
|
VAR *create_var(const char *name)
|
|
{
|
|
VAR *var,**walk;
|
|
|
|
var = malloc(sizeof(VAR));
|
|
if (!var) {
|
|
perror("malloc");
|
|
exit(1);
|
|
}
|
|
var->name = name; /* strdup'ed */
|
|
var->value.type = vt_none;
|
|
for (walk = &variables; *walk; walk = &(*walk)->next)
|
|
if (strcmp(name,(*walk)->name) > 0) break;
|
|
var->next = *walk;
|
|
*walk = var;
|
|
return var;
|
|
}
|
|
|
|
|
|
VAR *lookup(const char *name)
|
|
{
|
|
VAR *var;
|
|
|
|
for (var = variables; var; var = var->next)
|
|
if (!strcmp(var->name,name)) break;
|
|
return var;
|
|
}
|
|
|
|
|
|
static void destroy(VALUE value)
|
|
{
|
|
if (value.type == vt_text) free((char *) value.u.text);
|
|
}
|
|
|
|
|
|
void assign(VAR *var,VALUE value)
|
|
{
|
|
destroy(var->value);
|
|
var->value = value;
|
|
}
|
|
|
|
|
|
static int str2errno(const char *str)
|
|
{
|
|
const struct errno_table *walk;
|
|
|
|
for (walk = table; walk->name; walk++)
|
|
if (!strcmp(walk->name,str)) break;
|
|
return walk->value;
|
|
}
|
|
|
|
|
|
static VALUE convert(VALUE in,VALUE_TYPE type)
|
|
{
|
|
VALUE out;
|
|
char *end;
|
|
|
|
if (in.type == type) {
|
|
if (type == vt_text) {
|
|
in.u.text = strdup(in.u.text);
|
|
if (!in.u.text) {
|
|
perror("strdup");
|
|
exit(1);
|
|
}
|
|
}
|
|
return in;
|
|
}
|
|
if (in.type != vt_text) yyerror("type conflict");
|
|
out.type = type;
|
|
switch (type) {
|
|
case vt_vcc:
|
|
memset(&out.u.id,0,sizeof(out.u.id));
|
|
*(unsigned long *) &out.u.id = strtoul(in.u.text,&end,0);
|
|
if (*end) yyerror("invalid number");
|
|
break;
|
|
case vt_error:
|
|
out.u.num = strtoul(in.u.text,&end,0);
|
|
if (*end) {
|
|
out.u.num = str2errno(*in.u.text == '-' ? in.u.text+1 :
|
|
in.u.text);
|
|
if (!out.u.num) yyerror("invalid error code");
|
|
if (*in.u.text == '-') out.u.num = -out.u.num;
|
|
}
|
|
break;
|
|
case vt_svc:
|
|
if (text2atm(in.u.text,(struct sockaddr *) &out.u.svc,
|
|
sizeof(out.u.svc), T2A_SVC | T2A_NAME) < 0)
|
|
yyerror("invalid SVC address");
|
|
break;
|
|
case vt_pvc:
|
|
if (text2atm(in.u.text,(struct sockaddr *) &out.u.pvc,
|
|
sizeof(out.u.pvc),T2A_PVC | T2A_NNI | T2A_NAME) < 0)
|
|
yyerror("invalid PVC address");
|
|
break;
|
|
case vt_qos:
|
|
if (text2qos(in.u.text,&out.u.qos,0) < 0) yyerror("invalid QOS");
|
|
break;
|
|
case vt_sap:
|
|
if (text2sap(in.u.text,&out.u.sap,T2S_NAME) < 0)
|
|
yyerror("invalid SAP address");
|
|
break;
|
|
default:
|
|
fprintf(stderr,"unexpected conversion type %d\n",type);
|
|
exit(1);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
|
|
void check(VALUE a,VALUE b)
|
|
{
|
|
if (a.type == vt_text) a = convert(a,b.type);
|
|
if (b.type == vt_text) b = convert(b,a.type);
|
|
if (a.type != b.type) yyerror("type conflict");
|
|
switch (a.type) {
|
|
case vt_vcc:
|
|
case vt_error:
|
|
if (kptr_eq(&a.u.id,&b.u.id)) return;
|
|
break;
|
|
case vt_svc:
|
|
if (atm_equal((struct sockaddr *) &a.u.svc,
|
|
(struct sockaddr *) &b.u.svc,0,0)) return;
|
|
break;
|
|
case vt_pvc:
|
|
if (atm_equal((struct sockaddr *) &a.u.pvc,
|
|
(struct sockaddr *) &b.u.pvc,0,0)) return;
|
|
break;
|
|
case vt_qos:
|
|
if (qos_equal(&a.u.qos,&b.u.qos)) return;
|
|
break;
|
|
case vt_sap:
|
|
if (sap_equal(&a.u.sap,&b.u.sap,0)) return;
|
|
break;
|
|
default:
|
|
fprintf(stderr,"unexpected conversion type %d\n",a.type);
|
|
exit(1);
|
|
}
|
|
printf("Expected ");
|
|
print_value(b);
|
|
printf(",\nbut message contains ");
|
|
print_value(a);
|
|
printf("\n");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
#define COPY_MSG_VAL(V) \
|
|
switch (field) { \
|
|
case F_VCC: _COPY(V.u.id,msg->vcc); break; \
|
|
case F_LISTEN_VCC: _COPY(V.u.id,msg->listen_vcc); break; \
|
|
case F_REPLY: _COPY(V.u.num,msg->reply); break; \
|
|
case F_PVC: _COPY(V.u.pvc,msg->pvc); break; \
|
|
case F_LOCAL: _COPY(V.u.svc,msg->local); break; \
|
|
case F_QOS: _COPY(V.u.qos,msg->qos); break; \
|
|
case F_SVC: _COPY(V.u.svc,msg->svc); break; \
|
|
case F_SAP: _COPY(V.u.sap,msg->sap); break; \
|
|
default: fprintf(stderr,"unexpected field type 0x%x\n",field); \
|
|
exit(1); \
|
|
}
|
|
|
|
|
|
|
|
VALUE pick(const struct atmsvc_msg *msg,int field)
|
|
{
|
|
VALUE out;
|
|
|
|
if (msg->type >= MSG_TYPES) {
|
|
fprintf(stderr,"invalid message type %d",msg->type);
|
|
exit(1);
|
|
}
|
|
if (!(types[msg->type].fields & field)) yyerror("no such field in message");
|
|
out.type = type_of(field);
|
|
#define _COPY(V,M) V = M
|
|
COPY_MSG_VAL(out)
|
|
#undef _COPY
|
|
return out;
|
|
}
|
|
|
|
|
|
void store(struct atmsvc_msg *msg,int field,VALUE val)
|
|
{
|
|
if (msg->type >= MSG_TYPES) {
|
|
fprintf(stderr,"invalid message type %d",msg->type);
|
|
exit(1);
|
|
}
|
|
if (!(types[msg->type].fields & field)) yyerror("no such field in message");
|
|
if (val.type != type_of(field)) yyerror("type conflict");
|
|
#define _COPY(V,M) M = V
|
|
COPY_MSG_VAL(val)
|
|
#undef _COPY
|
|
}
|
|
|
|
|
|
#undef COPY_MSG_VAL
|
|
|
|
|
|
VALUE eval(VALUE_TYPE type,const char *str)
|
|
{
|
|
VALUE a,b;
|
|
|
|
a.type = vt_text;
|
|
a.u.text = strdup(str);
|
|
if (!a.u.text) {
|
|
perror("strdup");
|
|
exit(1);
|
|
}
|
|
b = convert(a,type);
|
|
destroy(a);
|
|
return b;
|
|
}
|
|
|
|
|
|
void cast(VAR *var,VALUE_TYPE type)
|
|
{
|
|
VALUE old;
|
|
|
|
if (var->value.type == type) return;
|
|
old = var->value;
|
|
var->value = convert(var->value,type);
|
|
destroy(old);
|
|
}
|
|
|
|
|
|
VALUE_TYPE type_of(int field)
|
|
{
|
|
switch (field) {
|
|
case F_VCC:
|
|
case F_LISTEN_VCC:
|
|
return vt_vcc;
|
|
case F_REPLY:
|
|
return vt_error;
|
|
case F_PVC:
|
|
return vt_pvc;
|
|
case F_LOCAL:
|
|
case F_SVC:
|
|
return vt_svc;
|
|
case F_QOS:
|
|
return vt_qos;
|
|
case F_SAP:
|
|
return vt_sap;
|
|
default:
|
|
fprintf(stderr,"unexpected field type 0x%x\n",field);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc,char **argv)
|
|
{
|
|
const char *name;
|
|
|
|
name = *argv;
|
|
if (argc > 2 && !strcmp(argv[1],"-q")) {
|
|
quiet = 1;
|
|
argc--;
|
|
argv++;
|
|
}
|
|
if (argc > 2 && !strcmp(argv[1],"-v")) {
|
|
verbose = 1;
|
|
argc--;
|
|
argv++;
|
|
}
|
|
if (argc != 2 || (quiet && verbose)) {
|
|
fprintf(stderr,"usage: %s [-q | -v] socket\n",name);
|
|
return 1;
|
|
}
|
|
sock = un_attach(argv[1]);
|
|
if (sock < 0) {
|
|
perror(argv[1]);
|
|
return 1;
|
|
}
|
|
return yyparse();
|
|
}
|