Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 1 | /* GTP Hub Implementation */ |
| 2 | |
| 3 | /* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> |
| 4 | * All Rights Reserved |
| 5 | * |
| 6 | * Author: Neels Hofmeyr |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU Affero General Public License as published by |
| 10 | * the Free Software Foundation; either version 3 of the License, or |
| 11 | * (at your option) any later version. |
| 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | * GNU Affero General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU Affero General Public License |
| 19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 20 | */ |
| 21 | |
| 22 | #pragma once |
| 23 | |
| 24 | #include <stdint.h> |
| 25 | #include <sys/socket.h> |
| 26 | |
| 27 | #include <osmocom/core/select.h> |
| 28 | #include <osmocom/core/timer.h> |
Neels Hofmeyr | 1ba50c6 | 2015-11-20 01:28:40 +0100 | [diff] [blame] | 29 | #include <osmocom/core/rate_ctr.h> |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 30 | |
Neels Hofmeyr | 30f7bcb | 2015-11-08 20:34:47 +0100 | [diff] [blame] | 31 | #include <openbsc/gprs_sgsn.h> |
| 32 | |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 33 | |
| 34 | /* support */ |
| 35 | |
| 36 | /* TODO move to osmocom/core/socket.c ? */ |
| 37 | #include <netdb.h> /* for IPPROTO_* etc */ |
| 38 | struct osmo_sockaddr { |
| 39 | struct sockaddr_storage a; |
| 40 | socklen_t l; |
| 41 | }; |
| 42 | |
| 43 | /* TODO move to osmocom/core/socket.c ? */ |
| 44 | /*! \brief Initialize a sockaddr |
| 45 | * \param[out] addr Valid osmo_sockaddr pointer to write result to |
| 46 | * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC |
| 47 | * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM |
| 48 | * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP |
| 49 | * \param[in] host Remote host name or IP address in string form |
| 50 | * \param[in] port Remote port number in host byte order |
| 51 | * \returns 0 on success, otherwise an error code (from getaddrinfo()). |
| 52 | * |
| 53 | * Copy the first result from a getaddrinfo() call with the given parameters to |
| 54 | * *addr and *addr_len. On error, do not change *addr and return nonzero. |
| 55 | */ |
| 56 | int osmo_sockaddr_init(struct osmo_sockaddr *addr, |
| 57 | uint16_t family, uint16_t type, uint8_t proto, |
| 58 | const char *host, uint16_t port); |
| 59 | |
| 60 | /* Conveniently pass AF_UNSPEC, SOCK_DGRAM and IPPROTO_UDP to |
| 61 | * osmo_sockaddr_init(). */ |
| 62 | static inline int osmo_sockaddr_init_udp(struct osmo_sockaddr *addr, |
| 63 | const char *host, uint16_t port) |
| 64 | { |
Neels Hofmeyr | 9cfe037 | 2015-11-16 14:52:05 +0100 | [diff] [blame] | 65 | return osmo_sockaddr_init(addr, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, |
| 66 | host, port); |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 67 | } |
| 68 | |
| 69 | /*! \brief convert sockaddr to human readable string. |
| 70 | * \param[out] addr_str Valid pointer to a buffer of length addr_str_len. |
| 71 | * \param[in] addr_str_len Size of buffer addr_str points at. |
| 72 | * \param[out] port_str Valid pointer to a buffer of length port_str_len. |
| 73 | * \param[in] port_str_len Size of buffer port_str points at. |
| 74 | * \param[in] addr Binary representation as returned by osmo_sockaddr_init(). |
| 75 | * \param[in] flags flags as passed to getnameinfo(). |
| 76 | * \returns 0 on success, an error code on error. |
| 77 | * |
| 78 | * Return the IPv4 or IPv6 address string and the port (a.k.a. service) string |
| 79 | * representations of the given struct osmo_sockaddr in two caller provided |
| 80 | * char buffers. Flags of (NI_NUMERICHOST | NI_NUMERICSERV) return numeric |
| 81 | * address and port. Either one of addr_str or port_str may be NULL, in which |
| 82 | * case nothing is returned there. |
| 83 | * |
| 84 | * See also osmo_sockaddr_to_str() (less flexible, but much more convenient). */ |
| 85 | int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len, |
| 86 | char *port_str, size_t port_str_len, |
| 87 | const struct osmo_sockaddr *addr, |
| 88 | int flags); |
| 89 | |
| 90 | |
Neels Hofmeyr | 9cfe037 | 2015-11-16 14:52:05 +0100 | [diff] [blame] | 91 | /*! \brief concatenate the parts returned by osmo_sockaddr_to_strs(). |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 92 | * \param[in] addr Binary representation as returned by osmo_sockaddr_init(). |
| 93 | * \param[in] buf A buffer to use for string operations. |
| 94 | * \param[in] buf_len Length of the buffer. |
| 95 | * \returns Address string (in buffer). |
| 96 | * |
| 97 | * Compose a string of the numeric IP-address and port represented by *addr of |
| 98 | * the form "<ip-addr> port <port>". The returned string is valid until the |
| 99 | * next invocation of this function. |
| 100 | */ |
| 101 | const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr, |
| 102 | char *buf, size_t buf_len); |
| 103 | |
| 104 | /*! \brief conveniently return osmo_sockaddr_to_strb() in a static buffer. |
| 105 | * \param[in] addr Binary representation as returned by osmo_sockaddr_init(). |
| 106 | * \returns Address string in static buffer. |
| 107 | * |
| 108 | * See osmo_sockaddr_to_strb(). |
| 109 | * |
| 110 | * Note: only one osmo_sockaddr_to_str() call will work per print/log |
| 111 | * statement. For two or more, use osmo_sockaddr_to_strb() with a separate |
| 112 | * buffer each. |
| 113 | */ |
| 114 | const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr); |
| 115 | |
| 116 | /*! \brief compare two osmo_sockaddr. |
| 117 | * \param[in] a The first address to compare. |
| 118 | * \param[in] b The other address to compare. |
| 119 | * \returns 0 if equal, otherwise -1 or 1. |
| 120 | */ |
Neels Hofmeyr | 9cfe037 | 2015-11-16 14:52:05 +0100 | [diff] [blame] | 121 | int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, |
| 122 | const struct osmo_sockaddr *b); |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 123 | |
| 124 | /*! \brief Overwrite *dst with *src. |
| 125 | * Like memcpy(), but copy only the valid bytes. */ |
Neels Hofmeyr | 9cfe037 | 2015-11-16 14:52:05 +0100 | [diff] [blame] | 126 | void osmo_sockaddr_copy(struct osmo_sockaddr *dst, |
| 127 | const struct osmo_sockaddr *src); |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 128 | |
| 129 | |
| 130 | /* general */ |
| 131 | |
| 132 | enum gtphub_plane_idx { |
| 133 | GTPH_PLANE_CTRL = 0, |
| 134 | GTPH_PLANE_USER = 1, |
| 135 | GTPH_PLANE_N |
| 136 | }; |
| 137 | |
Neels Hofmeyr | e54cd15 | 2015-11-24 13:31:06 +0100 | [diff] [blame] | 138 | enum gtphub_side_idx { |
Neels Hofmeyr | a9905a5 | 2015-11-29 23:49:48 +0100 | [diff] [blame] | 139 | GTPH_SIDE_SGSN = 0, |
| 140 | GTPH_SIDE_GGSN = 1, |
Neels Hofmeyr | e54cd15 | 2015-11-24 13:31:06 +0100 | [diff] [blame] | 141 | GTPH_SIDE_N |
| 142 | }; |
| 143 | |
Neels Hofmeyr | f977320 | 2015-11-27 00:05:56 +0100 | [diff] [blame] | 144 | #define for_each_side(I) for (I = 0; I < GTPH_SIDE_N; I++) |
| 145 | #define for_each_plane(I) for (I = 0; I < GTPH_PLANE_N; I++) |
| 146 | #define for_each_side_and_plane(I,J) for_each_side(I) for_each_plane(J) |
| 147 | |
Neels Hofmeyr | e54cd15 | 2015-11-24 13:31:06 +0100 | [diff] [blame] | 148 | static inline int other_side_idx(int side_idx) |
| 149 | { |
| 150 | return (side_idx + 1) & 1; |
| 151 | } |
| 152 | |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 153 | extern const char* const gtphub_plane_idx_names[GTPH_PLANE_N]; |
| 154 | extern const uint16_t gtphub_plane_idx_default_port[GTPH_PLANE_N]; |
| 155 | |
Neels Hofmeyr | a9905a5 | 2015-11-29 23:49:48 +0100 | [diff] [blame] | 156 | extern const char* const gtphub_side_idx_names[GTPH_SIDE_N]; |
| 157 | |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 158 | /* A host address in the form that is expected in the 7.7.32 GSN Address IE. |
| 159 | * len is either 4 (IPv4) or 16 (IPv6), any other value is invalid. If no |
| 160 | * address is set, len shall be 0. */ |
| 161 | struct gsn_addr { |
| 162 | uint16_t len; |
| 163 | uint8_t buf[16]; |
| 164 | }; |
| 165 | |
| 166 | void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src); |
| 167 | int gsn_addr_from_str(struct gsn_addr *gsna, const char *numeric_addr_str); |
| 168 | |
| 169 | /* Return gsna in numeric string form, in a static buffer. */ |
| 170 | const char *gsn_addr_to_str(const struct gsn_addr *gsna); |
| 171 | |
| 172 | /* note: strbuf_len doesn't need to be larger than INET6_ADDRSTRLEN + 1. */ |
| 173 | const char *gsn_addr_to_strb(const struct gsn_addr *gsna, |
| 174 | char *strbuf, int strbuf_len); |
| 175 | |
| 176 | /* Return 1 on match, zero otherwise. */ |
| 177 | int gsn_addr_same(const struct gsn_addr *a, const struct gsn_addr *b); |
| 178 | |
Neels Hofmeyr | c227594 | 2015-11-10 22:07:04 +0100 | [diff] [blame] | 179 | /* Decode sa to gsna. Return 0 on success. If port is non-NULL, the port number |
| 180 | * from sa is also returned. */ |
| 181 | int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port, |
| 182 | const struct osmo_sockaddr *sa); |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 183 | |
| 184 | /* expiry */ |
| 185 | |
| 186 | struct expiring_item; |
| 187 | typedef void (*del_cb_t)(struct expiring_item *); |
| 188 | |
| 189 | struct expiring_item { |
| 190 | struct llist_head entry; |
| 191 | time_t expiry; |
| 192 | del_cb_t del_cb; |
| 193 | }; |
| 194 | |
| 195 | struct expiry { |
| 196 | int expiry_in_seconds; |
| 197 | struct llist_head items; |
| 198 | }; |
| 199 | |
| 200 | /* Initialize an expiry queue. */ |
| 201 | void expiry_init(struct expiry *exq, int expiry_in_seconds); |
| 202 | |
Neels Hofmeyr | 9cfe037 | 2015-11-16 14:52:05 +0100 | [diff] [blame] | 203 | /* Add a new mapping, or restart the expiry timeout for an already listed |
| 204 | * mapping. */ |
Neels Hofmeyr | 231653a | 2015-11-24 13:23:44 +0100 | [diff] [blame] | 205 | void expiry_add(struct expiry *exq, struct expiring_item *item, time_t now); |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 206 | |
Neels Hofmeyr | 16c3f57 | 2015-11-11 17:27:01 +0100 | [diff] [blame] | 207 | /* Initialize to all-empty; must be called before using the item in any way. */ |
| 208 | void expiring_item_init(struct expiring_item *item); |
| 209 | |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 210 | /* Remove the given item from its expiry queue, and call item->del_cb, if set. |
| 211 | * This sets item->del_cb to NULL and is harmless when run a second time on the |
| 212 | * same item, so the del_cb may choose to call this function, too, to allow |
| 213 | * deleting items from several code paths. */ |
| 214 | void expiring_item_del(struct expiring_item *item); |
| 215 | |
| 216 | /* Carry out due expiry of mappings. Must be invoked regularly. |
| 217 | * 'now' is the current clock count in seconds and must correspond to the clock |
| 218 | * count passed to nr_map_add(). A monotonous clock counter should be used. */ |
| 219 | int expiry_tick(struct expiry *exq, time_t now); |
| 220 | |
Neels Hofmeyr | 20bd6bf | 2015-11-20 00:08:28 +0100 | [diff] [blame] | 221 | /* Expire all items. */ |
| 222 | void expiry_clear(struct expiry *exq); |
| 223 | |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 224 | |
| 225 | /* number map */ |
| 226 | |
| 227 | /* A number map assigns a "random" mapped number to each user provided number. |
| 228 | * If the same number is requested multiple times, the same mapped number is |
| 229 | * returned. |
| 230 | * |
| 231 | * Number maps plug into possibly shared pools and expiry queues, for example: |
| 232 | * |
| 233 | * mapA -----------+-> pool1 <-+-- mapB |
| 234 | * {10->1, 11->5} | {1, 2, 3, ...} | {10->2, 11->3} |
| 235 | * | | |
| 236 | * | | |
| 237 | * /-> \-> expiry1 <-/ |
| 238 | * | (30 seconds) |
| 239 | * | |
| 240 | * mapC -------+-----> pool2 <-+-- mapD |
| 241 | * {10->1, 11->3} {1, 2, 3, ...} | {10->2, 11->5} |
| 242 | * | |
| 243 | * expiry2 <-/ |
| 244 | * (60 seconds) |
| 245 | * |
| 246 | * A map contains mappings ("10->1"). Each map needs a number pool, which can |
| 247 | * be shared with other maps. Each new mapping receives a number from the pool, |
| 248 | * which is then unavailable to any other map using the same pool. |
| 249 | * |
| 250 | * A map may point at an expiry queue, in which case all mappings added to it |
| 251 | * are also appended to the expiry queue (using a separate llist entry in the |
| 252 | * mapping). Any number of maps may submit to the same expiry queue, if they |
| 253 | * desire the same expiry timeout. An expiry queue stores the mappings in |
| 254 | * chronological order, so that expiry checking is needed only from the start |
| 255 | * of the queue; hence only mappings with identical expiry timeout can be added |
| 256 | * to the same expiry queue. Upon expiry, a mapping is dropped from the map it |
| 257 | * was submitted at. expiry_tick() needs to be called regularly for each expiry |
| 258 | * queue. |
| 259 | * |
| 260 | * A nr_mapping can be embedded in a larger struct: each mapping can have a |
| 261 | * distinct destructor (del_cb), and each del_cb can figure out the container |
| 262 | * struct's address and free that upon expiry or manual deletion. So in expiry |
| 263 | * queues (and even maps), mappings of different container types can be mixed. |
| 264 | * This can help to drastically reduce the amount of unnecessary visits during |
| 265 | * expiry checking, for the case that no expiry is pending. An expiry queue |
| 266 | * always knows which mappings to expire next, because they are right at the |
| 267 | * start of its list. |
| 268 | * |
| 269 | * Mapping allocation and a del_cb are provided by the caller. If del_cb is |
| 270 | * NULL, no deallocation will be done (allowing statically allocated entries). |
| 271 | */ |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 272 | |
Neels Hofmeyr | 334af5d | 2015-11-17 14:24:46 +0100 | [diff] [blame] | 273 | typedef unsigned int nr_t; |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 274 | |
| 275 | /* Generator for unused numbers. So far this counts upwards from zero, but the |
| 276 | * implementation may change in the future. Treat this like an opaque struct. |
| 277 | * If this becomes random, the tests need to be fixed. */ |
| 278 | struct nr_pool { |
| 279 | nr_t last_nr; |
Neels Hofmeyr | e2ed8e6 | 2015-11-17 14:30:37 +0100 | [diff] [blame] | 280 | nr_t nr_min; |
| 281 | nr_t nr_max; |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 282 | }; |
| 283 | |
| 284 | struct nr_mapping { |
| 285 | struct llist_head entry; |
| 286 | struct expiring_item expiry_entry; |
| 287 | |
| 288 | void *origin; |
| 289 | nr_t orig; |
| 290 | nr_t repl; |
| 291 | }; |
| 292 | |
| 293 | struct nr_map { |
| 294 | struct nr_pool *pool; /* multiple nr_maps can share a nr_pool. */ |
| 295 | struct expiry *add_items_to_expiry; |
| 296 | struct llist_head mappings; |
| 297 | }; |
| 298 | |
| 299 | |
Neels Hofmeyr | e2ed8e6 | 2015-11-17 14:30:37 +0100 | [diff] [blame] | 300 | void nr_pool_init(struct nr_pool *pool, nr_t nr_min, nr_t nr_max); |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 301 | |
| 302 | /* Return the next unused number from the nr_pool. */ |
| 303 | nr_t nr_pool_next(struct nr_pool *pool); |
| 304 | |
| 305 | /* Initialize the nr_mapping to zero/empty values. */ |
| 306 | void nr_mapping_init(struct nr_mapping *mapping); |
| 307 | |
| 308 | /* Remove the given mapping from its parent map and expiry queue, and call |
| 309 | * mapping->del_cb, if set. */ |
| 310 | void nr_mapping_del(struct nr_mapping *mapping); |
| 311 | |
| 312 | /* Initialize an (already allocated) nr_map, and set the map's number pool. |
| 313 | * Multiple nr_map instances may use the same nr_pool. Set the nr_map's expiry |
| 314 | * queue to exq, so that all added mappings are automatically expired after the |
| 315 | * time configured in exq. exq may be NULL to disable automatic expiry. */ |
| 316 | void nr_map_init(struct nr_map *map, struct nr_pool *pool, |
| 317 | struct expiry *exq); |
| 318 | |
| 319 | /* Add a new entry to the map. mapping->orig, mapping->origin and |
| 320 | * mapping->del_cb must be set before calling this function. The remaining |
| 321 | * fields of *mapping will be overwritten. mapping->repl is set to the next |
| 322 | * available mapped number from map->pool. 'now' is the current clock count in |
| 323 | * seconds; if no map->expiry is used, just pass 0 for 'now'. */ |
| 324 | void nr_map_add(struct nr_map *map, struct nr_mapping *mapping, |
| 325 | time_t now); |
| 326 | |
Neels Hofmeyr | 508514c | 2015-11-24 13:30:38 +0100 | [diff] [blame] | 327 | /* Restart the timeout for the given mapping. mapping must be a member of map. |
| 328 | */ |
| 329 | void nr_map_refresh(struct nr_map *map, struct nr_mapping *mapping, |
| 330 | time_t now); |
| 331 | |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 332 | /* Return a known mapping from nr_orig and the given origin. If nr_orig is |
| 333 | * unknown, return NULL. */ |
| 334 | struct nr_mapping *nr_map_get(const struct nr_map *map, |
| 335 | void *origin, nr_t nr_orig); |
| 336 | |
| 337 | /* Return a known mapping to nr_repl. If nr_repl is unknown, return NULL. */ |
| 338 | struct nr_mapping *nr_map_get_inv(const struct nr_map *map, nr_t nr_repl); |
| 339 | |
| 340 | /* Remove all mappings from map. */ |
| 341 | void nr_map_clear(struct nr_map *map); |
| 342 | |
| 343 | /* Return 1 if map has no entries, 0 otherwise. */ |
| 344 | int nr_map_empty(const struct nr_map *map); |
| 345 | |
| 346 | |
| 347 | /* config */ |
| 348 | |
Neels Hofmeyr | 2c8b581 | 2015-11-25 16:45:59 +0100 | [diff] [blame] | 349 | static const int GTPH_EXPIRE_QUICKLY_SECS = 30; /* TODO is there a spec for this? */ |
| 350 | static const int GTPH_EXPIRE_SLOWLY_MINUTES = 6 * 60; /* TODO is there a spec for this? */ |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 351 | |
| 352 | struct gtphub_cfg_addr { |
| 353 | const char *addr_str; |
| 354 | uint16_t port; |
| 355 | }; |
| 356 | |
| 357 | struct gtphub_cfg_bind { |
| 358 | struct gtphub_cfg_addr bind; |
| 359 | }; |
| 360 | |
| 361 | struct gtphub_cfg { |
Neels Hofmeyr | a9905a5 | 2015-11-29 23:49:48 +0100 | [diff] [blame] | 362 | struct gtphub_cfg_bind to_gsns[GTPH_SIDE_N][GTPH_PLANE_N]; |
| 363 | struct gtphub_cfg_addr proxy[GTPH_SIDE_N][GTPH_PLANE_N]; |
Neels Hofmeyr | ca2361c | 2015-12-03 14:12:44 +0100 | [diff] [blame] | 364 | int sgsn_use_sender; /* Use sender, not GSN addr IE with std ports */ |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 365 | }; |
| 366 | |
| 367 | |
| 368 | /* state */ |
| 369 | |
| 370 | struct gtphub_peer { |
| 371 | struct llist_head entry; |
| 372 | |
| 373 | struct llist_head addresses; /* Alternatives, not load balancing. */ |
| 374 | struct nr_pool seq_pool; |
| 375 | struct nr_map seq_map; |
| 376 | }; |
| 377 | |
| 378 | struct gtphub_peer_addr { |
| 379 | struct llist_head entry; |
| 380 | |
| 381 | struct gtphub_peer *peer; |
| 382 | struct gsn_addr addr; |
| 383 | struct llist_head ports; |
| 384 | }; |
| 385 | |
| 386 | struct gtphub_peer_port { |
| 387 | struct llist_head entry; |
| 388 | |
| 389 | struct gtphub_peer_addr *peer_addr; |
| 390 | uint16_t port; |
| 391 | unsigned int ref_count; /* references from other peers' seq_maps */ |
Neels Hofmeyr | bc44330 | 2015-12-01 15:20:18 +0100 | [diff] [blame] | 392 | struct osmo_sockaddr sa; /* a "cache" for (peer_addr->addr, port) */ |
| 393 | int last_restart_count; /* 0..255 = valid, all else means unknown */ |
Neels Hofmeyr | e38fb66 | 2015-12-06 16:44:14 +0100 | [diff] [blame] | 394 | |
| 395 | struct rate_ctr_group *counters_io; |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 396 | }; |
| 397 | |
Neels Hofmeyr | e54cd15 | 2015-11-24 13:31:06 +0100 | [diff] [blame] | 398 | struct gtphub_tunnel_endpoint { |
| 399 | struct gtphub_peer_port *peer; |
| 400 | uint32_t tei_orig; /* from/to peer */ |
Neels Hofmeyr | e38fb66 | 2015-12-06 16:44:14 +0100 | [diff] [blame] | 401 | |
| 402 | struct rate_ctr_group *counters_io; |
Neels Hofmeyr | e54cd15 | 2015-11-24 13:31:06 +0100 | [diff] [blame] | 403 | }; |
| 404 | |
| 405 | struct gtphub_tunnel { |
| 406 | struct llist_head entry; |
| 407 | struct expiring_item expiry_entry; |
| 408 | |
Neels Hofmeyr | ee07e4f | 2015-12-06 19:11:45 +0100 | [diff] [blame] | 409 | uint32_t tei_repl; /* unique TEI to replace peers' TEIs */ |
Neels Hofmeyr | e54cd15 | 2015-11-24 13:31:06 +0100 | [diff] [blame] | 410 | struct gtphub_tunnel_endpoint endpoint[GTPH_SIDE_N][GTPH_PLANE_N]; |
| 411 | }; |
| 412 | |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 413 | struct gtphub_bind { |
| 414 | struct gsn_addr local_addr; |
Neels Hofmeyr | 4b2cbda | 2015-11-20 03:16:19 +0100 | [diff] [blame] | 415 | uint16_t local_port; |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 416 | struct osmo_fd ofd; |
| 417 | |
| 418 | /* list of struct gtphub_peer */ |
| 419 | struct llist_head peers; |
Neels Hofmeyr | 390e910 | 2015-11-16 13:45:13 +0100 | [diff] [blame] | 420 | |
| 421 | const char *label; /* For logging */ |
Neels Hofmeyr | 1ba50c6 | 2015-11-20 01:28:40 +0100 | [diff] [blame] | 422 | struct rate_ctr_group *counters_io; |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 423 | }; |
| 424 | |
Neels Hofmeyr | 30f7bcb | 2015-11-08 20:34:47 +0100 | [diff] [blame] | 425 | struct gtphub_resolved_ggsn { |
| 426 | struct llist_head entry; |
| 427 | struct expiring_item expiry_entry; |
| 428 | |
| 429 | /* The APN OI, the Operator Identifier, is the combined address, |
| 430 | * including parts of the IMSI and APN NI, and ending with ".gprs". */ |
| 431 | char apn_oi_str[GSM_APN_LENGTH]; |
| 432 | |
| 433 | /* Which address and port we resolved that to. */ |
| 434 | struct gtphub_peer_port *peer; |
| 435 | }; |
| 436 | |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 437 | struct gtphub { |
Neels Hofmeyr | a9905a5 | 2015-11-29 23:49:48 +0100 | [diff] [blame] | 438 | struct gtphub_bind to_gsns[GTPH_SIDE_N][GTPH_PLANE_N]; |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 439 | |
Neels Hofmeyr | a9905a5 | 2015-11-29 23:49:48 +0100 | [diff] [blame] | 440 | /* pointers to an entry of to_gsns[s][p].peers */ |
| 441 | struct gtphub_peer_port *proxy[GTPH_SIDE_N][GTPH_PLANE_N]; |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 442 | |
Neels Hofmeyr | e2ed8e6 | 2015-11-17 14:30:37 +0100 | [diff] [blame] | 443 | /* The TEI numbers will simply wrap and be reused, which will work out |
| 444 | * in practice. Problems would arise if one given peer maintained the |
| 445 | * same TEI for a time long enough for the TEI nr map to wrap an entire |
| 446 | * uint32_t; if a new TEI were mapped every second, this would take |
| 447 | * more than 100 years (in which a single given TEI must not time out) |
| 448 | * to cause a problem. */ |
Neels Hofmeyr | d121ea6 | 2015-11-27 01:20:53 +0100 | [diff] [blame] | 449 | struct nr_pool tei_pool; |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 450 | |
Neels Hofmeyr | e54cd15 | 2015-11-24 13:31:06 +0100 | [diff] [blame] | 451 | struct llist_head tunnels; /* struct gtphub_tunnel */ |
Neels Hofmeyr | 10fc024 | 2015-12-01 00:23:45 +0100 | [diff] [blame] | 452 | struct llist_head pending_deletes; /* opaque (gtphub.c) */ |
Neels Hofmeyr | e54cd15 | 2015-11-24 13:31:06 +0100 | [diff] [blame] | 453 | |
Neels Hofmeyr | 4960fab | 2015-11-18 17:53:00 +0100 | [diff] [blame] | 454 | struct llist_head ggsn_lookups; /* opaque (gtphub_ares.c) */ |
Neels Hofmeyr | 30f7bcb | 2015-11-08 20:34:47 +0100 | [diff] [blame] | 455 | struct llist_head resolved_ggsns; /* struct gtphub_resolved_ggsn */ |
| 456 | |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 457 | struct osmo_timer_list gc_timer; |
Neels Hofmeyr | 2c8b581 | 2015-11-25 16:45:59 +0100 | [diff] [blame] | 458 | struct expiry expire_quickly; |
| 459 | struct expiry expire_slowly; |
Neels Hofmeyr | bb3d678 | 2015-11-09 15:12:25 +0100 | [diff] [blame] | 460 | |
Neels Hofmeyr | ba9e9f6 | 2015-11-26 22:19:22 +0100 | [diff] [blame] | 461 | uint8_t restart_counter; |
Neels Hofmeyr | ca2361c | 2015-12-03 14:12:44 +0100 | [diff] [blame] | 462 | |
| 463 | int sgsn_use_sender; |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 464 | }; |
| 465 | |
| 466 | struct gtp_packet_desc; |
| 467 | |
| 468 | |
| 469 | /* api */ |
| 470 | |
Neels Hofmeyr | 4b2cbda | 2015-11-20 03:16:19 +0100 | [diff] [blame] | 471 | int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg); |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 472 | int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file); |
| 473 | |
| 474 | /* Initialize and start gtphub: bind to ports, run expiry timers. */ |
Neels Hofmeyr | ba9e9f6 | 2015-11-26 22:19:22 +0100 | [diff] [blame] | 475 | int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg, |
| 476 | uint8_t restart_counter); |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 477 | |
Neels Hofmeyr | 20bd6bf | 2015-11-20 00:08:28 +0100 | [diff] [blame] | 478 | /* Close all sockets, expire all maps and peers and free all allocations. The |
| 479 | * struct is then unusable, unless gtphub_start() is run on it again. */ |
| 480 | void gtphub_stop(struct gtphub *hub); |
| 481 | |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 482 | time_t gtphub_now(void); |
| 483 | |
| 484 | /* Remove expired items, empty peers, ... */ |
| 485 | void gtphub_gc(struct gtphub *hub, time_t now); |
| 486 | |
| 487 | /* Return the string of the first address for this peer. */ |
| 488 | const char *gtphub_peer_str(struct gtphub_peer *peer); |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 489 | |
Neels Hofmeyr | e54cd15 | 2015-11-24 13:31:06 +0100 | [diff] [blame] | 490 | /* Return a human readable description of tun in a static buffer. */ |
| 491 | const char *gtphub_tunnel_str(struct gtphub_tunnel *tun); |
| 492 | |
| 493 | /* Return 1 if all of tun's endpoints are fully established, 0 otherwise. */ |
| 494 | int gtphub_tunnel_complete(struct gtphub_tunnel *tun); |
| 495 | |
Neels Hofmeyr | a9905a5 | 2015-11-29 23:49:48 +0100 | [diff] [blame] | 496 | int gtphub_handle_buf(struct gtphub *hub, |
| 497 | unsigned int side_idx, |
| 498 | unsigned int port_idx, |
| 499 | const struct osmo_sockaddr *from_addr, |
| 500 | uint8_t *buf, |
| 501 | size_t received, |
| 502 | time_t now, |
| 503 | uint8_t **reply_buf, |
| 504 | struct osmo_fd **to_ofd, |
| 505 | struct osmo_sockaddr *to_addr); |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 506 | |
Neels Hofmeyr | 30f7bcb | 2015-11-08 20:34:47 +0100 | [diff] [blame] | 507 | struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub, |
| 508 | struct gtphub_bind *bind, |
| 509 | const struct gsn_addr *addr, |
| 510 | uint16_t port); |
| 511 | |
Neels Hofmeyr | c8a614d | 2015-09-24 17:32:30 +0200 | [diff] [blame] | 512 | struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind, |
| 513 | const struct osmo_sockaddr *addr); |
Neels Hofmeyr | 30f7bcb | 2015-11-08 20:34:47 +0100 | [diff] [blame] | 514 | |
| 515 | void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str, |
| 516 | struct gsn_addr *resolved_addr, |
| 517 | time_t now); |
| 518 | |
| 519 | const char *gtphub_port_str(struct gtphub_peer_port *port); |
Neels Hofmeyr | 996ec1d | 2015-12-02 15:43:10 +0100 | [diff] [blame] | 520 | |
| 521 | int gtphub_write(const struct osmo_fd *to, |
| 522 | const struct osmo_sockaddr *to_addr, |
| 523 | const uint8_t *buf, size_t buf_len); |