layer23: Split [2/2] -> The header files
[osmocom-bb.git] / src / host / layer23 / src / mobile / subscriber.c
1 /*
2  * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
3  *
4  * All Rights Reserved
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  */
21
22 #include <stdint.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <osmocore/talloc.h>
26
27 #include <osmocom/bb/common/logging.h>
28 #include <osmocom/bb/common/osmocom_data.h>
29 #include <osmocom/bb/common/networks.h>
30
31 void *l23_ctx;
32
33 int gsm_subscr_init(struct osmocom_ms *ms)
34 {
35         struct gsm_subscriber *subscr = &ms->subscr;
36
37         memset(subscr, 0, sizeof(*subscr));
38         subscr->ms = ms;
39
40         /* set key invalid */
41         subscr->key_seq = 7;
42
43         /* init lists */
44         INIT_LLIST_HEAD(&subscr->plmn_list);
45         INIT_LLIST_HEAD(&subscr->plmn_na);
46
47         return 0;
48 }
49
50 int gsm_subscr_exit(struct osmocom_ms *ms)
51 {
52         struct gsm_subscriber *subscr = &ms->subscr;
53         struct llist_head *lh, *lh2;
54
55         /* flush lists */
56         llist_for_each_safe(lh, lh2, &subscr->plmn_list) {
57                 llist_del(lh);
58                 talloc_free(lh);
59         }
60         llist_for_each_safe(lh, lh2, &subscr->plmn_na) {
61                 llist_del(lh);
62                 talloc_free(lh);
63         }
64
65         return 0;
66 }
67
68 /* Attach test card, no sim must be present */
69 int gsm_subscr_testcard(struct osmocom_ms *ms, uint16_t mcc, uint16_t mnc)
70 {
71         struct gsm_settings *set = &ms->settings;
72         struct gsm_subscriber *subscr = &ms->subscr;
73         struct msgb *msg;
74         char *error;
75
76         if (subscr->sim_valid) {
77                 LOGP(DMM, LOGL_ERROR, "Cannot insert card, until current card "
78                         "is detached.\n");
79                 return -EBUSY;
80         }
81
82         error = gsm_check_imsi(set->test_imsi);
83         if (error) {
84                 LOGP(DMM, LOGL_ERROR, "%s\n", error);
85                 return -EINVAL;
86         }
87
88         /* reset subscriber */
89         gsm_subscr_exit(ms);
90         gsm_subscr_init(ms);
91
92         sprintf(subscr->sim_name, "test");
93         // TODO: load / save SIM to file system
94         subscr->sim_valid = 1;
95         subscr->ustate = GSM_SIM_U2_NOT_UPDATED;
96         subscr->acc_barr = set->test_barr; /* we may access barred cell */
97         subscr->acc_class = 0xffff; /* we have any access class */
98         subscr->plmn_valid = set->test_rplmn_valid;
99         subscr->plmn_mcc = mcc;
100         subscr->plmn_mnc = mnc;
101         subscr->always_search_hplmn = set->test_always;
102         subscr->t6m_hplmn = 1; /* try to find home network every 6 min */
103         strcpy(subscr->imsi, set->test_imsi);
104
105         LOGP(DMM, LOGL_INFO, "(ms %s) Inserting test card (IMSI=%s %s, %s)\n",
106                 ms->name, subscr->imsi, gsm_imsi_mcc(subscr->imsi),
107                 gsm_imsi_mnc(subscr->imsi));
108
109         LOGP(DMM, LOGL_INFO, "-> Test card regisered to %s %s (%s, %s)\n",
110                 gsm_print_mcc(mcc), gsm_print_mnc(mnc), gsm_get_mcc(mcc), 
111                 gsm_get_mnc(mcc, mnc));
112
113         /* insert card */
114         msg = gsm48_mmr_msgb_alloc(GSM48_MMR_REG_REQ);
115         if (!msg)
116                 return -ENOMEM;
117         gsm48_mmr_downmsg(ms, msg);
118
119         return 0;
120 }
121
122 /* Detach card */
123 int gsm_subscr_remove(struct osmocom_ms *ms)
124 {
125         struct gsm_subscriber *subscr = &ms->subscr;
126         struct msgb *msg;
127
128         if (!subscr->sim_valid) {
129                 LOGP(DMM, LOGL_ERROR, "Cannot remove card, no card present\n");
130                 return -EINVAL;
131         }
132
133         /* remove card */
134         msg = gsm48_mmr_msgb_alloc(GSM48_MMR_NREG_REQ);
135         if (!msg)
136                 return -ENOMEM;
137         gsm48_mmr_downmsg(ms, msg);
138
139         return 0;
140 }
141
142 static const char *subscr_ustate_names[] = {
143         "U0_NULL",
144         "U1_UPDATED",
145         "U2_NOT_UPDATED",
146         "U3_ROAMING_NA"
147 };
148
149 /* change to new U state */
150 void new_sim_ustate(struct gsm_subscriber *subscr, int state)
151 {
152         LOGP(DMM, LOGL_INFO, "(ms %s) new state %s -> %s\n", subscr->ms->name,
153                 subscr_ustate_names[subscr->ustate],
154                 subscr_ustate_names[state]);
155
156         subscr->ustate = state;
157 }
158
159 /* del forbidden PLMN */
160 int gsm_subscr_del_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
161         uint16_t mnc)
162 {
163         struct gsm_sub_plmn_na *na;
164
165         llist_for_each_entry(na, &subscr->plmn_na, entry) {
166                 if (na->mcc == mcc && na->mnc == mnc) {
167                         LOGP(DPLMN, LOGL_INFO, "Delete from list of forbidden "
168                                 "PLMNs (mcc=%s, mnc=%s)\n",
169                                 gsm_print_mcc(mcc), gsm_print_mnc(mnc));
170                         llist_del(&na->entry);
171                         talloc_free(na);
172 #ifdef TODO
173                         update plmn not allowed list on sim
174 #endif
175                         return 0;
176                 }
177         }
178
179         return -EINVAL;
180 }
181
182 /* add forbidden PLMN */
183 int gsm_subscr_add_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
184                                         uint16_t mnc, uint8_t cause)
185 {
186         struct gsm_sub_plmn_na *na;
187
188         LOGP(DPLMN, LOGL_INFO, "Add to list of forbidden PLMNs "
189                 "(mcc=%s, mnc=%s)\n", gsm_print_mcc(mcc), gsm_print_mnc(mnc));
190         na = talloc_zero(l23_ctx, struct gsm_sub_plmn_na);
191         if (!na)
192                 return -ENOMEM;
193         na->mcc = mcc;
194         na->mnc = mnc;
195         na->cause = cause;
196         llist_add_tail(&na->entry, &subscr->plmn_na);
197
198         /* don't add Home PLMN to SIM */
199         if (subscr->sim_valid && gsm_match_mnc(mcc, mnc, subscr->imsi))
200                 return -EINVAL;
201
202 #ifdef TODO
203         update plmn not allowed list on sim
204 #endif
205
206         return 0;
207 }
208
209 /* search forbidden PLMN */
210 int gsm_subscr_is_forbidden_plmn(struct gsm_subscriber *subscr, uint16_t mcc,
211                                         uint16_t mnc)
212 {
213         struct gsm_sub_plmn_na *na;
214
215         llist_for_each_entry(na, &subscr->plmn_na, entry) {
216                 if (na->mcc == mcc && na->mnc == mnc)
217                         return 1;
218         }
219
220         return 0;
221 }
222
223 int gsm_subscr_dump_forbidden_plmn(struct osmocom_ms *ms,
224                         void (*print)(void *, const char *, ...), void *priv)
225 {
226         struct gsm_subscriber *subscr = &ms->subscr;
227         struct gsm_sub_plmn_na *temp;
228
229         print(priv, "MCC    |MNC    |cause\n");
230         print(priv, "-------+-------+-------\n");
231         llist_for_each_entry(temp, &subscr->plmn_na, entry)
232                 print(priv, "%s    |%s%s    |#%d\n",
233                         gsm_print_mcc(temp->mcc), gsm_print_mnc(temp->mnc),
234                         ((temp->mnc & 0x00f) == 0x00f) ? " ":"", temp->cause);
235
236         return 0;
237 }
238
239 /* dump subscriber */
240 void gsm_subscr_dump(struct gsm_subscriber *subscr,
241                         void (*print)(void *, const char *, ...), void *priv)
242 {
243         int i;
244         struct gsm_sub_plmn_list *plmn_list;
245         struct gsm_sub_plmn_na *plmn_na;
246
247         print(priv, "Mobile Subscriber of MS '%s':\n", subscr->ms->name);
248
249         if (!subscr->sim_valid) {
250                 print(priv, " No SIM present.\n");
251                 return;
252         }
253
254         print(priv, " IMSI: %s\n", subscr->imsi);
255         print(priv, " Status: %s  IMSI %s", subscr_ustate_names[subscr->ustate],
256                 (subscr->imsi_attached) ? "attached" : "detached");
257         if (subscr->tmsi_valid)
258                 print(priv, "  TSMI  %08x", subscr->tmsi);
259         if (subscr->lai_valid)
260                 print(priv, "  LAI: MCC %s  MNC %s  LAC 0x%04x  (%s, %s)\n",
261                         gsm_print_mcc(subscr->lai_mcc),
262                         gsm_print_mnc(subscr->lai_mnc), subscr->lai_lac,
263                         gsm_get_mcc(subscr->lai_mcc),
264                         gsm_get_mnc(subscr->lai_mcc, subscr->lai_mnc));
265         else
266                 print(priv, "  LAI: invalid\n");
267         if (subscr->key_seq != 7) {
268                 print(priv, " Key: sequence %d ");
269                 for (i = 0; i < sizeof(subscr->key); i++)
270                         print(priv, " %02x", subscr->key[i]);
271                 print(priv, "\n");
272         }
273         if (subscr->plmn_valid)
274                 print(priv, " Current PLMN: MCC %s  MNC %s  (%s, %s)\n",
275                         gsm_print_mcc(subscr->plmn_mcc),
276                         gsm_print_mnc(subscr->plmn_mnc),
277                         gsm_get_mcc(subscr->plmn_mcc),
278                         gsm_get_mnc(subscr->plmn_mcc, subscr->plmn_mnc));
279         print(priv, " Access barred cells: %s\n",
280                 (subscr->acc_barr) ? "yes" : "no");
281         print(priv, " Access classes:");
282         for (i = 0; i < 16; i++)
283                 if ((subscr->acc_class & (1 << i)))
284                         print(priv, " C%d", i);
285         print(priv, "\n");
286         if (!llist_empty(&subscr->plmn_list)) {
287                 print(priv, " List of preferred PLMNs:\n");
288                 print(priv, "        MCC    |MNC\n");
289                 print(priv, "        -------+-------\n");
290                 llist_for_each_entry(plmn_list, &subscr->plmn_list, entry)
291                         print(priv, "        %s    |%s\n",
292                         gsm_print_mcc(plmn_list->mcc),
293                         gsm_print_mnc(plmn_list->mnc));
294         }
295         if (!llist_empty(&subscr->plmn_na)) {
296                 print(priv, " List of forbidden PLMNs:\n");
297                 print(priv, "        MCC    |MNC    |cause\n");
298                 print(priv, "        -------+-------+-------\n");
299                 llist_for_each_entry(plmn_na, &subscr->plmn_na, entry)
300                         print(priv, "        %s    |%s%s    |#%d\n",
301                                 gsm_print_mcc(plmn_na->mcc),
302                                 gsm_print_mnc(plmn_na->mnc),
303                                 ((plmn_na->mnc & 0x00f) == 0x00f) ? " ":"",
304                                 plmn_na->cause);
305         }
306 }
307
308 char *gsm_check_imsi(const char *imsi)
309 {
310         int i;
311
312         if (!imsi || strlen(imsi) != 15)
313                 return "IMSI must have 15 digits!";
314
315         for (i = 0; i < strlen(imsi); i++) {
316                 if (imsi[i] < '0' || imsi[i] > '9')
317                         return "IMSI must have digits 0 to 9 only!";
318         }
319
320         return NULL;
321 }
322
323