Oliver Smith | bf7deda | 2019-11-20 10:56:35 +0100 | [diff] [blame] | 1 | /* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> |
| 2 | * |
| 3 | * All Rights Reserved |
| 4 | * |
| 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU General Public License as published by |
| 7 | * the Free Software Foundation; either version 2 of the License, or |
| 8 | * (at your option) any later version. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License along |
| 16 | * with this program. If not, see <http://www.gnu.org/licenses/>. |
| 17 | */ |
| 18 | |
| 19 | #include <osmocom/hlr/logging.h> |
| 20 | #include <osmocom/mslookup/mslookup_client.h> |
| 21 | #include <osmocom/mslookup/mslookup_client_fake.h> |
| 22 | |
| 23 | #include <string.h> |
| 24 | |
| 25 | /* Fake mslookup method */ |
| 26 | |
| 27 | struct fake_lookup_state { |
| 28 | struct osmo_mslookup_client *client; |
| 29 | struct llist_head requests; |
| 30 | struct osmo_timer_list async_response_timer; |
| 31 | struct osmo_mslookup_fake_response *responses; |
| 32 | size_t responses_len; |
| 33 | }; |
| 34 | |
| 35 | struct fake_lookup_request { |
| 36 | struct llist_head entry; |
| 37 | uint32_t request_handle; |
| 38 | struct osmo_mslookup_query query; |
| 39 | struct timeval received_at; |
| 40 | }; |
| 41 | |
| 42 | /*! Args for osmo_timer_schedule: seconds and microseconds. */ |
| 43 | #define ASYNC_RESPONSE_PERIOD 0, (1e6 / 10) |
| 44 | static void fake_lookup_async_response(void *state); |
| 45 | |
| 46 | static void fake_lookup_request(struct osmo_mslookup_client_method *method, |
| 47 | const struct osmo_mslookup_query *query, |
| 48 | uint32_t request_handle) |
| 49 | { |
| 50 | struct fake_lookup_state *state = method->priv; |
| 51 | char buf[256]; |
| 52 | LOGP(DMSLOOKUP, LOGL_DEBUG, "%s(%s)\n", __func__, osmo_mslookup_result_name_b(buf, sizeof(buf), query, NULL)); |
| 53 | |
| 54 | /* A real implementation would send packets to some remote server. |
| 55 | * Here this is simulated: add to the list of requests, which fake_lookup_async_response() will reply upon |
| 56 | * according to the test data listing the replies that the test wants to generate. */ |
| 57 | |
| 58 | struct fake_lookup_request *r = talloc_zero(method->client, struct fake_lookup_request); |
| 59 | *r = (struct fake_lookup_request){ |
| 60 | .request_handle = request_handle, |
| 61 | .query = *query, |
| 62 | }; |
| 63 | osmo_gettimeofday(&r->received_at, NULL); |
| 64 | llist_add_tail(&r->entry, &state->requests); |
| 65 | } |
| 66 | |
| 67 | static void fake_lookup_request_cleanup(struct osmo_mslookup_client_method *method, |
| 68 | uint32_t request_handle) |
| 69 | { |
| 70 | struct fake_lookup_state *state = method->priv; |
| 71 | |
| 72 | /* Tear down any state associated with this handle. */ |
| 73 | struct fake_lookup_request *r; |
| 74 | llist_for_each_entry(r, &state->requests, entry) { |
| 75 | if (r->request_handle != request_handle) |
| 76 | continue; |
| 77 | llist_del(&r->entry); |
| 78 | talloc_free(r); |
| 79 | LOGP(DMSLOOKUP, LOGL_DEBUG, "%s() ok\n", __func__); |
| 80 | return; |
| 81 | } |
| 82 | LOGP(DMSLOOKUP, LOGL_DEBUG, "%s() FAILED\n", __func__); |
| 83 | } |
| 84 | |
| 85 | static void fake_lookup_async_response(void *data) |
| 86 | { |
| 87 | struct fake_lookup_state *state = data; |
| 88 | struct fake_lookup_request *req, *n; |
| 89 | struct timeval now; |
| 90 | char str[256]; |
| 91 | |
| 92 | osmo_gettimeofday(&now, NULL); |
| 93 | |
| 94 | llist_for_each_entry_safe(req, n, &state->requests, entry) { |
| 95 | struct osmo_mslookup_fake_response *resp; |
| 96 | |
| 97 | for (resp = state->responses; |
| 98 | (resp - state->responses) < state->responses_len; |
| 99 | resp++) { |
| 100 | struct timeval diff; |
| 101 | |
| 102 | if (resp->sent) |
| 103 | continue; |
| 104 | if (osmo_mslookup_id_cmp(&req->query.id, &resp->for_id) != 0) |
| 105 | continue; |
| 106 | if (strcmp(req->query.service, resp->for_service) != 0) |
| 107 | continue; |
| 108 | |
| 109 | timersub(&now, &req->received_at, &diff); |
| 110 | if (timercmp(&diff, &resp->time_to_reply, <)) |
| 111 | continue; |
| 112 | |
| 113 | /* It's time to reply to this request. */ |
| 114 | LOGP(DMSLOOKUP, LOGL_DEBUG, "osmo_mslookup_client_rx_result(): %s\n", |
| 115 | osmo_mslookup_result_name_b(str, sizeof(str), &req->query, &resp->result)); |
| 116 | osmo_mslookup_client_rx_result(state->client, req->request_handle, &resp->result); |
| 117 | resp->sent = true; |
| 118 | |
| 119 | /* The req will have been cleaned up now, so we must not iterate over state->responses anymore |
| 120 | * with this req. */ |
| 121 | break; |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | osmo_timer_schedule(&state->async_response_timer, ASYNC_RESPONSE_PERIOD); |
| 126 | } |
| 127 | |
| 128 | struct osmo_mslookup_client_method *osmo_mslookup_client_add_fake(struct osmo_mslookup_client *client, |
| 129 | struct osmo_mslookup_fake_response *responses, |
| 130 | size_t responses_len) |
| 131 | { |
| 132 | struct osmo_mslookup_client_method *method = talloc_zero(client, struct osmo_mslookup_client_method); |
| 133 | OSMO_ASSERT(method); |
| 134 | |
| 135 | struct fake_lookup_state *state = talloc_zero(method, struct fake_lookup_state); |
| 136 | OSMO_ASSERT(state); |
| 137 | *state = (struct fake_lookup_state){ |
| 138 | .client = client, |
| 139 | .responses = responses, |
| 140 | .responses_len = responses_len, |
| 141 | }; |
| 142 | INIT_LLIST_HEAD(&state->requests); |
| 143 | |
| 144 | *method = (struct osmo_mslookup_client_method){ |
| 145 | .name = "fake", |
| 146 | .priv = state, |
| 147 | .request = fake_lookup_request, |
| 148 | .request_cleanup = fake_lookup_request_cleanup, |
| 149 | }; |
| 150 | |
| 151 | osmo_timer_setup(&state->async_response_timer, fake_lookup_async_response, state); |
| 152 | osmo_mslookup_client_method_add(client, method); |
| 153 | |
| 154 | osmo_timer_schedule(&state->async_response_timer, ASYNC_RESPONSE_PERIOD); |
| 155 | return method; |
| 156 | } |