blob: baa7e52178330f220020c090a8b9e72f0eec69ba [file] [log] [blame]
Harald Welte5540c4c2010-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>
Harald Welteea92ac32010-05-19 15:53:22 +020032#include <netinet/ip.h>
Harald Welte5540c4c2010-05-19 14:38:50 +020033#include <arpa/inet.h>
34
35#include <osmocore/select.h>
36#include <osmocore/msgb.h>
37#include <osmocore/talloc.h>
38
39#include <openbsc/socket.h>
40#include <openbsc/debug.h>
41#include <openbsc/gprs_ns.h>
42
43#define GRE_PTYPE_FR 0x6559
44
45struct gre_hdr {
46 uint16_t flags;
47 uint16_t ptype;
48} __attribute__ ((packed));
49
50static struct msgb *read_nsfrgre_msg(struct bsc_fd *bfd, int *error,
51 struct sockaddr_in *saddr)
52{
53 struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
54 int ret = 0;
55 socklen_t saddr_len = sizeof(*saddr);
Harald Welteea92ac32010-05-19 15:53:22 +020056 struct iphdr *iph;
Harald Welte5540c4c2010-05-19 14:38:50 +020057 struct gre_hdr *greh;
58 uint8_t *frh;
59 uint32_t dlci;
60
61 if (!msg) {
62 *error = -ENOMEM;
63 return NULL;
64 }
65
66 ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
67 (struct sockaddr *)saddr, &saddr_len);
68 if (ret < 0) {
69 LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
70 strerror(errno));
71 *error = ret;
Harald Welteffe191c2010-05-19 15:46:49 +020072 goto out_err;
Harald Welte5540c4c2010-05-19 14:38:50 +020073 } else if (ret == 0) {
Harald Welte5540c4c2010-05-19 14:38:50 +020074 *error = ret;
Harald Welteffe191c2010-05-19 15:46:49 +020075 goto out_err;
Harald Welte5540c4c2010-05-19 14:38:50 +020076 }
77
78 msgb_put(msg, ret);
79
Harald Welteea92ac32010-05-19 15:53:22 +020080 if (msg->len < sizeof(*iph) + sizeof(*greh) + 2) {
81 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
Harald Welte5540c4c2010-05-19 14:38:50 +020082 *error = -EIO;
83 goto out_err;
84 }
85
Harald Welteea92ac32010-05-19 15:53:22 +020086 iph = (struct iphdr *) msg->data;
87 if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) {
88 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
89 *error = -EIO;
90 goto out_err;
91 }
92
93 greh = (struct gre_hdr *) (msg->data + iph->ihl*4);
Harald Welte5540c4c2010-05-19 14:38:50 +020094 if (greh->flags) {
95 LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n",
96 ntohs(greh->flags));
97 }
98 if (greh->ptype != htons(GRE_PTYPE_FR)) {
99 LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n",
100 ntohs(greh->ptype));
101 *error = -EIO;
102 goto out_err;
103 }
104
105 if (msg->len < sizeof(*greh) + 2) {
106 LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len);
107 *error = -EIO;
108 goto out_err;
109 }
110
Harald Welteea92ac32010-05-19 15:53:22 +0200111 frh = (uint8_t *)greh + sizeof(*greh);
Harald Welte5540c4c2010-05-19 14:38:50 +0200112 if (frh[0] & 0x01) {
113 LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n");
114 *error = -EIO;
115 goto out_err;
116 }
117 dlci = (frh[0] & 0xfc << 2);
118 if ((frh[1] & 0x0f) != 0x01) {
119 LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n",
120 frh[1]);
121 *error = -EIO;
122 goto out_err;
123 }
124 dlci |= frh[1] >> 4;
125 if (dlci > 0xffff) {
126 LOGP(DNS, LOGL_ERROR, "We don't support DLCI > 65535 (%u)\n",
127 dlci);
128 *error = -EINVAL;
129 goto out_err;
130 }
131
Harald Welteea92ac32010-05-19 15:53:22 +0200132 msg->l2h = frh+2;
Harald Welte5540c4c2010-05-19 14:38:50 +0200133
134 /* Store DLCI in NETWORK BYTEORDER in sockaddr port member */
135 saddr->sin_port = htons(dlci & 0xffff);
136
137 return msg;
138
139out_err:
140 msgb_free(msg);
141 return NULL;
142}
143
144int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
145 struct sockaddr_in *saddr, enum gprs_ns_ll ll);
146
147static int handle_nsfrgre_read(struct bsc_fd *bfd)
148{
149 int error;
150 struct sockaddr_in saddr;
151 struct gprs_ns_inst *nsi = bfd->data;
152 struct msgb *msg = read_nsfrgre_msg(bfd, &error, &saddr);
153
154 if (!msg)
155 return error;
156
157 error = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE);
158
159 msgb_free(msg);
160
161 return error;
162}
163
164static int handle_nsfrgre_write(struct bsc_fd *bfd)
165{
166 /* FIXME: actually send the data here instead of nsip_sendmsg() */
167 return -EIO;
168}
169
170int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg)
171{
172 int rc;
173 struct gprs_ns_inst *nsi = nsvc->nsi;
174 struct sockaddr_in daddr;
175 uint16_t dlci = ntohs(nsvc->frgre.bts_addr.sin_port);
176 uint8_t *frh;
177 struct gre_hdr *greh;
178
179 /* Build socket address for the packet destionation */
Harald Welte3c2a88d2010-05-19 15:37:34 +0200180 daddr.sin_family = AF_INET;
Harald Welte5540c4c2010-05-19 14:38:50 +0200181 daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr;
182 daddr.sin_port = IPPROTO_GRE;
183
184 /* Prepend the FR header */
Harald Welte3c2a88d2010-05-19 15:37:34 +0200185 frh = msgb_push(msg, 2);
Harald Welte5540c4c2010-05-19 14:38:50 +0200186 frh[0] = (dlci >> 2) & 0xfc;
Harald Welte3c2a88d2010-05-19 15:37:34 +0200187 frh[1] = ((dlci & 0xf)<<4) | 0x01;
Harald Welte5540c4c2010-05-19 14:38:50 +0200188
189 /* Prepend the GRE header */
190 greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh));
191 greh->flags = 0;
Harald Welte3c2a88d2010-05-19 15:37:34 +0200192 greh->ptype = htons(GRE_PTYPE_FR);
Harald Welte5540c4c2010-05-19 14:38:50 +0200193
194 rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0,
195 (struct sockaddr *)&daddr, sizeof(daddr));
196
197 talloc_free(msg);
198
199 return rc;
200}
201
202static int nsfrgre_fd_cb(struct bsc_fd *bfd, unsigned int what)
203{
204 int rc = 0;
205
206 if (what & BSC_FD_READ)
207 rc = handle_nsfrgre_read(bfd);
208 if (what & BSC_FD_WRITE)
209 rc = handle_nsfrgre_write(bfd);
210
211 return rc;
212}
213
Harald Welteff3bde82010-05-19 15:09:09 +0200214int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi)
Harald Welte5540c4c2010-05-19 14:38:50 +0200215{
216 int rc;
217
218 /* Make sure we close any existing socket before changing it */
219 if (nsi->frgre.fd.fd)
220 close(nsi->frgre.fd.fd);
221
Harald Welteff3bde82010-05-19 15:09:09 +0200222 if (!nsi->frgre.enabled)
223 return 0;
224
225 rc = make_sock(&nsi->frgre.fd, IPPROTO_GRE, nsi->frgre.local_ip,
226 0, nsfrgre_fd_cb);
Harald Welte5540c4c2010-05-19 14:38:50 +0200227 if (rc < 0) {
228 LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n",
229 strerror(errno));
230 return rc;
231 }
232 nsi->frgre.fd.data = nsi;
233
234 return rc;
235}