1/*
2 * q_atm.c		ATM.
3 *
4 * Hacked 1998-2000 by Werner Almesberger, EPFL ICA
5 *
6 */
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <unistd.h>
11#include <ctype.h>
12#include <syslog.h>
13#include <fcntl.h>
14#include <sys/socket.h>
15#include <sys/ioctl.h>
16#include <netinet/in.h>
17#include <arpa/inet.h>
18#include <string.h>
19#include <atm.h>
20#include <linux/atmdev.h>
21#include <linux/atmarp.h>
22
23#include "utils.h"
24#include "tc_util.h"
25
26
27#define MAX_HDR_LEN 64
28
29
30static int atm_parse_opt(struct qdisc_util *qu, int argc, char **argv, struct nlmsghdr *n)
31{
32	if (argc) {
33		fprintf(stderr, "Usage: atm\n");
34		return -1;
35	}
36	return 0;
37}
38
39
40static void explain(void)
41{
42	fprintf(stderr, "Usage: ... atm ( pvc ADDR | svc ADDR [ sap SAP ] ) [ qos QOS ] [ sndbuf BYTES ]\n");
43	fprintf(stderr, "  [ hdr HEX... ] [ excess ( CLASSID | clp ) ] [ clip ]\n");
44}
45
46
47static int atm_parse_class_opt(struct qdisc_util *qu, int argc, char **argv,
48   struct nlmsghdr *n)
49{
50	struct sockaddr_atmsvc addr = {};
51	struct atm_qos qos;
52	struct atm_sap sap;
53	unsigned char hdr[MAX_HDR_LEN];
54	__u32 excess = 0;
55	struct rtattr *tail;
56	int sndbuf = 0;
57	int hdr_len = -1;
58	int set_clip = 0;
59	int s;
60
61	(void) text2qos("aal5,ubr:sdu=9180,rx:none", &qos, 0);
62	(void) text2sap("blli:l2=iso8802", &sap, 0);
63	while (argc > 0) {
64		if (!strcmp(*argv, "pvc")) {
65			NEXT_ARG();
66			if (text2atm(*argv, (struct sockaddr *) &addr,
67			    sizeof(addr), T2A_PVC | T2A_NAME) < 0) {
68				explain();
69				return -1;
70			}
71		} else if (!strcmp(*argv,"svc")) {
72			NEXT_ARG();
73			if (text2atm(*argv, (struct sockaddr *) &addr,
74			    sizeof(addr), T2A_SVC | T2A_NAME) < 0) {
75				explain();
76				return -1;
77			}
78		} else if (!strcmp(*argv,"qos")) {
79			NEXT_ARG();
80			if (text2qos(*argv, &qos, 0) < 0) {
81				explain();
82				return -1;
83			}
84		} else if (!strcmp(*argv,"sndbuf")) {
85			char *end;
86
87			NEXT_ARG();
88			sndbuf = strtol(*argv, &end, 0);
89			if (*end) {
90				explain();
91				return -1;
92			}
93		} else if (!strcmp(*argv,"sap")) {
94			NEXT_ARG();
95			if (addr.sas_family != AF_ATMSVC ||
96			    text2sap(*argv, &sap, T2A_NAME) < 0) {
97				explain();
98				return -1;
99			}
100		} else if (!strcmp(*argv,"hdr")) {
101			unsigned char *ptr;
102			char *walk;
103
104			NEXT_ARG();
105			ptr = hdr;
106			for (walk = *argv; *walk; walk++) {
107				int tmp;
108
109				if (ptr == hdr+MAX_HDR_LEN) {
110					fprintf(stderr, "header is too long\n");
111					return -1;
112				}
113				if (*walk == '.') continue;
114				if (!isxdigit(walk[0]) || !walk[1] ||
115				    !isxdigit(walk[1])) {
116					explain();
117					return -1;
118				}
119				sscanf(walk, "%2x", &tmp);
120				*ptr++ = tmp;
121				walk++;
122			}
123			hdr_len = ptr-hdr;
124		} else if (!strcmp(*argv,"excess")) {
125			NEXT_ARG();
126			if (!strcmp(*argv, "clp")) excess = 0;
127			else if (get_tc_classid(&excess, *argv)) {
128					explain();
129					return -1;
130				}
131		} else if (!strcmp(*argv,"clip")) {
132			set_clip = 1;
133		} else {
134			explain();
135			return 1;
136		}
137		argc--;
138		argv++;
139	}
140	s = socket(addr.sas_family, SOCK_DGRAM, 0);
141	if (s < 0) {
142		perror("socket");
143		return -1;
144	}
145	if (setsockopt(s, SOL_ATM, SO_ATMQOS, &qos, sizeof(qos)) < 0) {
146		perror("SO_ATMQOS");
147		return -1;
148	}
149	if (sndbuf)
150	    if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) {
151		perror("SO_SNDBUF");
152	    return -1;
153	}
154	if (addr.sas_family == AF_ATMSVC && setsockopt(s, SOL_ATM, SO_ATMSAP,
155	    &sap, sizeof(sap)) < 0) {
156		perror("SO_ATMSAP");
157		return -1;
158	}
159	if (connect(s, (struct sockaddr *) &addr, addr.sas_family == AF_ATMPVC ?
160	    sizeof(struct sockaddr_atmpvc) : sizeof(addr)) < 0) {
161		perror("connect");
162		return -1;
163	}
164	if (set_clip)
165		if (ioctl(s, ATMARP_MKIP, 0) < 0) {
166			perror("ioctl ATMARP_MKIP");
167			return -1;
168		}
169	tail = NLMSG_TAIL(n);
170	addattr_l(n, 1024, TCA_OPTIONS, NULL, 0);
171	addattr_l(n, 1024, TCA_ATM_FD, &s, sizeof(s));
172	if (excess) addattr_l(n, 1024, TCA_ATM_EXCESS, &excess, sizeof(excess));
173	if (hdr_len != -1) addattr_l(n, 1024, TCA_ATM_HDR, hdr, hdr_len);
174	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
175	return 0;
176}
177
178
179
180static int atm_print_opt(struct qdisc_util *qu, FILE *f, struct rtattr *opt)
181{
182	struct rtattr *tb[TCA_ATM_MAX+1];
183	char buffer[MAX_ATM_ADDR_LEN+1];
184
185	if (opt == NULL)
186		return 0;
187
188	parse_rtattr_nested(tb, TCA_ATM_MAX, opt);
189	if (tb[TCA_ATM_ADDR]) {
190		if (RTA_PAYLOAD(tb[TCA_ATM_ADDR]) <
191		    sizeof(struct sockaddr_atmpvc))
192			fprintf(stderr, "ATM: address too short\n");
193		else {
194			if (atm2text(buffer, MAX_ATM_ADDR_LEN,
195			    RTA_DATA(tb[TCA_ATM_ADDR]), A2T_PRETTY | A2T_NAME) <
196			    0) fprintf(stderr, "atm2text error\n");
197			fprintf(f, "pvc %s ", buffer);
198		}
199	}
200	if (tb[TCA_ATM_HDR]) {
201		int i;
202		const __u8 *hdr = RTA_DATA(tb[TCA_ATM_HDR]);
203
204		fprintf(f, "hdr");
205		for (i = 0; i < RTA_PAYLOAD(tb[TCA_ATM_HDR]); i++)
206			fprintf(f, "%c%02x", i ? '.' : ' ', hdr[i]);
207		if (!i) fprintf(f, " .");
208		fprintf(f, " ");
209	}
210	if (tb[TCA_ATM_EXCESS]) {
211		__u32 excess;
212
213		if (RTA_PAYLOAD(tb[TCA_ATM_EXCESS]) < sizeof(excess))
214			fprintf(stderr, "ATM: excess class ID too short\n");
215		else {
216			excess = rta_getattr_u32(tb[TCA_ATM_EXCESS]);
217			if (!excess) fprintf(f, "excess clp ");
218			else {
219				char buf[64];
220
221				print_tc_classid(buf, sizeof(buf), excess);
222				fprintf(f, "excess %s ", buf);
223			}
224		}
225	}
226	if (tb[TCA_ATM_STATE]) {
227		static const char *map[] = { ATM_VS2TXT_MAP };
228		int state;
229
230		if (RTA_PAYLOAD(tb[TCA_ATM_STATE]) < sizeof(state))
231			fprintf(stderr, "ATM: state field too short\n");
232		else {
233			state = rta_getattr_u32(tb[TCA_ATM_STATE]);
234			fprintf(f, "%s ", map[state]);
235		}
236	}
237	return 0;
238}
239
240
241struct qdisc_util atm_qdisc_util = {
242	.id		= "atm",
243	.parse_qopt	= atm_parse_opt,
244	.print_qopt	= atm_print_opt,
245	.parse_copt	= atm_parse_class_opt,
246	.print_copt	= atm_print_opt,
247};
248