blob: 7367b6a6d96fd9d445fa5dea8bd548e58c959782 [file] [log] [blame]
Harald Welteb3ee2652010-05-19 14:38:50 +02001/* GPRS Networks Service (NS) messages on the Gb interface
2 * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */
3
4/* NS-over-FR-over-GRE implementation */
5
6/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
7 *
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 *
24 */
25
26#include <errno.h>
27#include <string.h>
28#include <unistd.h>
29
30#include <sys/socket.h>
31#include <netinet/in.h>
32#include <arpa/inet.h>
33
34#include <osmocore/select.h>
35#include <osmocore/msgb.h>
36#include <osmocore/talloc.h>
37
38#include <openbsc/socket.h>
39#include <openbsc/debug.h>
40#include <openbsc/gprs_ns.h>
41
42#define GRE_PTYPE_FR 0x6559
43
44struct gre_hdr {
45 uint16_t flags;
46 uint16_t ptype;
47} __attribute__ ((packed));
48
49static struct msgb *read_nsfrgre_msg(struct bsc_fd *bfd, int *error,
50 struct sockaddr_in *saddr)
51{
52 struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
53 int ret = 0;
54 socklen_t saddr_len = sizeof(*saddr);
55 struct gre_hdr *greh;
56 uint8_t *frh;
57 uint32_t dlci;
58
59 if (!msg) {
60 *error = -ENOMEM;
61 return NULL;
62 }
63
64 ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
65 (struct sockaddr *)saddr, &saddr_len);
66 if (ret < 0) {
67 LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
68 strerror(errno));
69 *error = ret;
70 return NULL;
71 } else if (ret == 0) {
72 msgb_free(msg);
73 *error = ret;
74 return NULL;
75 }
76
77 msgb_put(msg, ret);
78
79 if (msg->len < sizeof(*greh)) {
80 LOGP(DNS, LOGL_ERROR, "Short GRE packet: %u bytes\n", msg->len);
81 *error = -EIO;
82 goto out_err;
83 }
84
85 greh = (struct gre_hdr *) msg->data;
86 if (greh->flags) {
87 LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n",
88 ntohs(greh->flags));
89 }
90 if (greh->ptype != htons(GRE_PTYPE_FR)) {
91 LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n",
92 ntohs(greh->ptype));
93 *error = -EIO;
94 goto out_err;
95 }
96
97 if (msg->len < sizeof(*greh) + 2) {
98 LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len);
99 *error = -EIO;
100 goto out_err;
101 }
102
103 frh = msg->data + sizeof(*greh);
104 if (frh[0] & 0x01) {
105 LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n");
106 *error = -EIO;
107 goto out_err;
108 }
109 dlci = (frh[0] & 0xfc << 2);
110 if ((frh[1] & 0x0f) != 0x01) {
111 LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n",
112 frh[1]);
113 *error = -EIO;
114 goto out_err;
115 }
116 dlci |= frh[1] >> 4;
117 if (dlci > 0xffff) {
118 LOGP(DNS, LOGL_ERROR, "We don't support DLCI > 65535 (%u)\n",
119 dlci);
120 *error = -EINVAL;
121 goto out_err;
122 }
123
124 msg->l2h = msg->data + sizeof(*greh) + 2;
125
126 /* Store DLCI in NETWORK BYTEORDER in sockaddr port member */
127 saddr->sin_port = htons(dlci & 0xffff);
128
129 return msg;
130
131out_err:
132 msgb_free(msg);
133 return NULL;
134}
135
136int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
137 struct sockaddr_in *saddr, enum gprs_ns_ll ll);
138
139static int handle_nsfrgre_read(struct bsc_fd *bfd)
140{
141 int error;
142 struct sockaddr_in saddr;
143 struct gprs_ns_inst *nsi = bfd->data;
144 struct msgb *msg = read_nsfrgre_msg(bfd, &error, &saddr);
145
146 if (!msg)
147 return error;
148
149 error = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE);
150
151 msgb_free(msg);
152
153 return error;
154}
155
156static int handle_nsfrgre_write(struct bsc_fd *bfd)
157{
158 /* FIXME: actually send the data here instead of nsip_sendmsg() */
159 return -EIO;
160}
161
162int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg)
163{
164 int rc;
165 struct gprs_ns_inst *nsi = nsvc->nsi;
166 struct sockaddr_in daddr;
167 uint16_t dlci = ntohs(nsvc->frgre.bts_addr.sin_port);
168 uint8_t *frh;
169 struct gre_hdr *greh;
170
171 /* Build socket address for the packet destionation */
Harald Welteac914b82010-05-19 15:37:34 +0200172 daddr.sin_family = AF_INET;
Harald Welteb3ee2652010-05-19 14:38:50 +0200173 daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr;
174 daddr.sin_port = IPPROTO_GRE;
175
176 /* Prepend the FR header */
Harald Welteac914b82010-05-19 15:37:34 +0200177 frh = msgb_push(msg, 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200178 frh[0] = (dlci >> 2) & 0xfc;
Harald Welteac914b82010-05-19 15:37:34 +0200179 frh[1] = ((dlci & 0xf)<<4) | 0x01;
Harald Welteb3ee2652010-05-19 14:38:50 +0200180
181 /* Prepend the GRE header */
182 greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh));
183 greh->flags = 0;
Harald Welteac914b82010-05-19 15:37:34 +0200184 greh->ptype = htons(GRE_PTYPE_FR);
Harald Welteb3ee2652010-05-19 14:38:50 +0200185
186 rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0,
187 (struct sockaddr *)&daddr, sizeof(daddr));
188
189 talloc_free(msg);
190
191 return rc;
192}
193
194static int nsfrgre_fd_cb(struct bsc_fd *bfd, unsigned int what)
195{
196 int rc = 0;
197
198 if (what & BSC_FD_READ)
199 rc = handle_nsfrgre_read(bfd);
200 if (what & BSC_FD_WRITE)
201 rc = handle_nsfrgre_write(bfd);
202
203 return rc;
204}
205
Harald Welte7fb05232010-05-19 15:09:09 +0200206int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi)
Harald Welteb3ee2652010-05-19 14:38:50 +0200207{
208 int rc;
209
210 /* Make sure we close any existing socket before changing it */
211 if (nsi->frgre.fd.fd)
212 close(nsi->frgre.fd.fd);
213
Harald Welte7fb05232010-05-19 15:09:09 +0200214 if (!nsi->frgre.enabled)
215 return 0;
216
217 rc = make_sock(&nsi->frgre.fd, IPPROTO_GRE, nsi->frgre.local_ip,
218 0, nsfrgre_fd_cb);
Harald Welteb3ee2652010-05-19 14:38:50 +0200219 if (rc < 0) {
220 LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n",
221 strerror(errno));
222 return rc;
223 }
224 nsi->frgre.fd.data = nsi;
225
226 return rc;
227}