|
1 Index: modules/lcr/lcr_mod.c |
|
2 diff -Nau modules/lcr/lcr_mod.c.orig modules/lcr/lcr_mod.c |
|
3 --- modules/lcr/lcr_mod.c.orig 2010-01-18 12:32:29.697648000 +0100 |
|
4 +++ modules/lcr/lcr_mod.c 2010-02-10 19:52:14.838272303 +0100 |
|
5 @@ -114,9 +114,18 @@ |
|
6 |
|
7 #define PRIORITY_COL "priority" |
|
8 |
|
9 +#define USER_COL "user" |
|
10 + |
|
11 +#define REALM_COL "realm" |
|
12 + |
|
13 +#define PASSWD_COL "passwd" |
|
14 + |
|
15 #define MAX_NO_OF_GWS 32 |
|
16 #define MAX_NO_OF_LCRS 256 |
|
17 #define MAX_PREFIX_LEN 256 |
|
18 +#define MAX_USER_LEN 64 |
|
19 +#define MAX_REALM_LEN 64 |
|
20 +#define MAX_PASSWD_LEN 64 |
|
21 #define MAX_TAG_LEN 16 |
|
22 #define MAX_FROM_URI_LEN 256 |
|
23 |
|
24 @@ -141,6 +150,12 @@ |
|
25 char tag[MAX_TAG_LEN + 1]; |
|
26 unsigned short tag_len; |
|
27 unsigned int flags; |
|
28 + char user[MAX_USER_LEN]; |
|
29 + unsigned short user_len; |
|
30 + char realm[MAX_REALM_LEN]; |
|
31 + unsigned short realm_len; |
|
32 + char passwd[MAX_PASSWD_LEN]; |
|
33 + unsigned short passwd_len; |
|
34 }; |
|
35 |
|
36 struct lcr_info { |
|
37 @@ -196,6 +211,9 @@ |
|
38 static str prefix_col = str_init(PREFIX_COL); |
|
39 static str from_uri_col = str_init(FROM_URI_COL); |
|
40 static str priority_col = str_init(PRIORITY_COL); |
|
41 +static str user_col = str_init(USER_COL); |
|
42 +static str realm_col = str_init(REALM_COL); |
|
43 +static str passwd_col = str_init(PASSWD_COL); |
|
44 |
|
45 /* timer */ |
|
46 int fr_inv_timer = DEF_FR_INV_TIMER; |
|
47 @@ -208,6 +226,9 @@ |
|
48 static char *contact_avp_param = NULL; |
|
49 static char *rpid_avp_param = NULL; |
|
50 static char *flags_avp_param = NULL; |
|
51 +static char *user_avp_param = NULL; |
|
52 +static char *realm_avp_param = NULL; |
|
53 +static char *passwd_avp_param = NULL; |
|
54 |
|
55 /* prefix mode */ |
|
56 int prefix_mode_param = DEF_PREFIX_MODE; |
|
57 @@ -239,6 +260,12 @@ |
|
58 static int_str rpid_avp; |
|
59 static int flags_avp_type; |
|
60 static int_str flags_avp; |
|
61 +static int user_avp_type; |
|
62 +static int_str user_avp; |
|
63 +static int realm_avp_type; |
|
64 +static int_str realm_avp; |
|
65 +static int passwd_avp_type; |
|
66 +static int_str passwd_avp; |
|
67 |
|
68 struct gw_info **gws; /* Pointer to current gw table pointer */ |
|
69 struct gw_info *gws_1; /* Pointer to gw table 1 */ |
|
70 @@ -327,6 +354,12 @@ |
|
71 {"fr_inv_timer", INT_PARAM, &fr_inv_timer }, |
|
72 {"fr_inv_timer_next", INT_PARAM, &fr_inv_timer_next }, |
|
73 {"prefix_mode", INT_PARAM, &prefix_mode_param }, |
|
74 + {"user_column", STR_PARAM, &user_col.s }, |
|
75 + {"realm_column", STR_PARAM, &realm_col.s }, |
|
76 + {"passwd_column", STR_PARAM, &passwd_col.s }, |
|
77 + {"auth_username_avp", STR_PARAM, &user_avp_param }, |
|
78 + {"auth_realm_avp", STR_PARAM, &realm_avp_param }, |
|
79 + {"auth_password_avp", STR_PARAM, &passwd_avp_param }, |
|
80 {0, 0, 0} |
|
81 }; |
|
82 |
|
83 @@ -438,6 +471,9 @@ |
|
84 prefix_col.len = strlen(prefix_col.s); |
|
85 from_uri_col.len = strlen(from_uri_col.s); |
|
86 priority_col.len = strlen(priority_col.s); |
|
87 + user_col.len = strlen(user_col.s); |
|
88 + realm_col.len = strlen(realm_col.s); |
|
89 + passwd_col.len = strlen(passwd_col.s); |
|
90 |
|
91 /* Bind database */ |
|
92 if (lcr_db_bind(&db_url)) { |
|
93 @@ -563,6 +599,60 @@ |
|
94 return -1; |
|
95 } |
|
96 |
|
97 + if (user_avp_param && *user_avp_param) { |
|
98 + s.s = user_avp_param; s.len = strlen(s.s); |
|
99 + if (pv_parse_spec(&s, &avp_spec)==0 |
|
100 + || avp_spec.type!=PVT_AVP) { |
|
101 + LM_ERR("Malformed or non AVP definition <%s>\n", user_avp_param); |
|
102 + return -1; |
|
103 + } |
|
104 + |
|
105 + if(pv_get_avp_name(0, &(avp_spec.pvp), &user_avp, &avp_flags)!=0) { |
|
106 + LM_ERR("Invalid AVP definition <%s>\n", user_avp_param); |
|
107 + return -1; |
|
108 + } |
|
109 + user_avp_type = avp_flags; |
|
110 + } else { |
|
111 + LM_ERR("AVP user_avp has not been defined\n"); |
|
112 + return -1; |
|
113 + } |
|
114 + |
|
115 + if (realm_avp_param && *realm_avp_param) { |
|
116 + s.s = realm_avp_param; s.len = strlen(s.s); |
|
117 + if (pv_parse_spec(&s, &avp_spec)==0 |
|
118 + || avp_spec.type!=PVT_AVP) { |
|
119 + LM_ERR("Malformed or non AVP definition <%s>\n", realm_avp_param); |
|
120 + return -1; |
|
121 + } |
|
122 + |
|
123 + if(pv_get_avp_name(0, &(avp_spec.pvp), &realm_avp, &avp_flags)!=0) { |
|
124 + LM_ERR("Invalid AVP definition <%s>\n", realm_avp_param); |
|
125 + return -1; |
|
126 + } |
|
127 + realm_avp_type = avp_flags; |
|
128 + } else { |
|
129 + LM_ERR("AVP realm_avp has not been defined\n"); |
|
130 + return -1; |
|
131 + } |
|
132 + |
|
133 + if (passwd_avp_param && *passwd_avp_param) { |
|
134 + s.s = passwd_avp_param; s.len = strlen(s.s); |
|
135 + if (pv_parse_spec(&s, &avp_spec)==0 |
|
136 + || avp_spec.type!=PVT_AVP) { |
|
137 + LM_ERR("Malformed or non AVP definition <%s>\n", passwd_avp_param); |
|
138 + return -1; |
|
139 + } |
|
140 + |
|
141 + if(pv_get_avp_name(0, &(avp_spec.pvp), &passwd_avp, &avp_flags)!=0) { |
|
142 + LM_ERR("Invalid AVP definition <%s>\n", passwd_avp_param); |
|
143 + return -1; |
|
144 + } |
|
145 + passwd_avp_type = avp_flags; |
|
146 + } else { |
|
147 + LM_ERR("AVP passwd_avp has not been defined\n"); |
|
148 + return -1; |
|
149 + } |
|
150 + |
|
151 /* Check table version */ |
|
152 db_con_t* dbh; |
|
153 if (lcr_dbf.init==0){ |
|
154 @@ -801,16 +891,17 @@ |
|
155 int reload_gws(void) |
|
156 { |
|
157 unsigned int i, port, strip, tag_len, prefix_len, from_uri_len, |
|
158 - grp_id, priority; |
|
159 + user_len, realm_len, passwd_len, grp_id, priority; |
|
160 struct in_addr ip_addr; |
|
161 unsigned int flags; |
|
162 uri_type scheme; |
|
163 uri_transport transport; |
|
164 db_con_t* dbh; |
|
165 char *tag, *prefix, *from_uri; |
|
166 + char *user, *realm, *passwd; |
|
167 db_res_t* res = NULL; |
|
168 db_row_t* row; |
|
169 - db_key_t gw_cols[8]; |
|
170 + db_key_t gw_cols[11]; |
|
171 db_key_t lcr_cols[4]; |
|
172 |
|
173 gw_cols[0] = &ip_addr_col; |
|
174 @@ -823,6 +914,9 @@ |
|
175 in the two tables? (ge vw lcr) */ |
|
176 gw_cols[6] = &grp_id_col; |
|
177 gw_cols[7] = &flags_col; |
|
178 + gw_cols[8] = &user_col; |
|
179 + gw_cols[9] = &realm_col; |
|
180 + gw_cols[10] = &passwd_col; |
|
181 |
|
182 lcr_cols[0] = &prefix_col; |
|
183 lcr_cols[1] = &from_uri_col; |
|
184 @@ -846,7 +940,7 @@ |
|
185 return -1; |
|
186 } |
|
187 |
|
188 - if (lcr_dbf.query(dbh, NULL, 0, NULL, gw_cols, 0, 8, 0, &res) < 0) { |
|
189 + if (lcr_dbf.query(dbh, NULL, 0, NULL, gw_cols, 0, 11, 0, &res) < 0) { |
|
190 LM_ERR("Failed to query gw data\n"); |
|
191 lcr_dbf.close(dbh); |
|
192 return -1; |
|
193 @@ -938,6 +1032,45 @@ |
|
194 lcr_dbf.close(dbh); |
|
195 return -1; |
|
196 } |
|
197 + if (VAL_NULL(ROW_VALUES(row) + 8) == 1) { |
|
198 + user_len = 0; |
|
199 + user = (char *)0; |
|
200 + } else { |
|
201 + user = (char *)VAL_STRING(ROW_VALUES(row) + 8); |
|
202 + user_len = strlen(user); |
|
203 + if (user_len > MAX_USER_LEN) { |
|
204 + LM_ERR("Too long gw user <%u>\n", user_len); |
|
205 + lcr_dbf.free_result(dbh, res); |
|
206 + lcr_dbf.close(dbh); |
|
207 + return -1; |
|
208 + } |
|
209 + } |
|
210 + if (VAL_NULL(ROW_VALUES(row) + 9) == 1) { |
|
211 + realm_len = 0; |
|
212 + realm = (char *)0; |
|
213 + } else { |
|
214 + realm = (char *)VAL_STRING(ROW_VALUES(row) + 9); |
|
215 + realm_len = strlen(realm); |
|
216 + if (realm_len > MAX_REALM_LEN) { |
|
217 + LM_ERR("Too long gw realm <%u>\n", realm_len); |
|
218 + lcr_dbf.free_result(dbh, res); |
|
219 + lcr_dbf.close(dbh); |
|
220 + return -1; |
|
221 + } |
|
222 + } |
|
223 + if (VAL_NULL(ROW_VALUES(row) + 10) == 1) { |
|
224 + passwd_len = 0; |
|
225 + passwd = (char *)0; |
|
226 + } else { |
|
227 + passwd = (char *)VAL_STRING(ROW_VALUES(row) + 10); |
|
228 + passwd_len = strlen(passwd); |
|
229 + if (passwd_len > MAX_PASSWD_LEN) { |
|
230 + LM_ERR("Too long gw passwd <%u>\n", passwd_len); |
|
231 + lcr_dbf.free_result(dbh, res); |
|
232 + lcr_dbf.close(dbh); |
|
233 + return -1; |
|
234 + } |
|
235 + } |
|
236 if (*gws == gws_1) { |
|
237 gws_2[i].ip_addr = (unsigned int)ip_addr.s_addr; |
|
238 gws_2[i].port = port; |
|
239 @@ -949,6 +1082,15 @@ |
|
240 gws_2[i].tag_len = tag_len; |
|
241 if (tag_len) |
|
242 memcpy(&(gws_2[i].tag[0]), tag, tag_len); |
|
243 + gws_2[i].user_len = user_len; |
|
244 + if (user_len) |
|
245 + memcpy(&(gws_2[i].user[0]), user, user_len); |
|
246 + gws_2[i].realm_len = realm_len; |
|
247 + if (realm_len) |
|
248 + memcpy(&(gws_2[i].realm[0]), realm, realm_len); |
|
249 + gws_2[i].passwd_len = passwd_len; |
|
250 + if (passwd_len) |
|
251 + memcpy(&(gws_2[i].passwd[0]), passwd, passwd_len); |
|
252 } else { |
|
253 gws_1[i].ip_addr = (unsigned int)ip_addr.s_addr; |
|
254 gws_1[i].port = port; |
|
255 @@ -960,6 +1102,15 @@ |
|
256 gws_1[i].tag_len = tag_len; |
|
257 if (tag_len) |
|
258 memcpy(&(gws_1[i].tag[0]), tag, tag_len); |
|
259 + gws_1[i].user_len = user_len; |
|
260 + if (user_len) |
|
261 + memcpy(&(gws_1[i].user[0]), user, user_len); |
|
262 + gws_1[i].realm_len = realm_len; |
|
263 + if (realm_len) |
|
264 + memcpy(&(gws_1[i].realm[0]), realm, realm_len); |
|
265 + gws_1[i].passwd_len = passwd_len; |
|
266 + if (passwd_len) |
|
267 + memcpy(&(gws_1[i].passwd[0]), passwd, passwd_len); |
|
268 } |
|
269 } |
|
270 |
|
271 @@ -1141,6 +1292,21 @@ |
|
272 attr = add_mi_attr(node, MI_DUP_VALUE, "FLAGS", 5, p, len); |
|
273 if(attr == NULL) |
|
274 return -1; |
|
275 + |
|
276 + attr = add_mi_attr(node, MI_DUP_VALUE, "USER", 6, |
|
277 + (*gws)[i].user, (*gws)[i].user_len ); |
|
278 + if(attr == NULL) |
|
279 + return -1; |
|
280 + |
|
281 + attr = add_mi_attr(node, MI_DUP_VALUE, "REALM", 6, |
|
282 + (*gws)[i].realm, (*gws)[i].realm_len ); |
|
283 + if(attr == NULL) |
|
284 + return -1; |
|
285 + |
|
286 + attr = add_mi_attr(node, MI_DUP_VALUE, "PASSWD", 6, |
|
287 + (*gws)[i].passwd, (*gws)[i].passwd_len ); |
|
288 + if(attr == NULL) |
|
289 + return -1; |
|
290 } |
|
291 |
|
292 for (i = 0; i < MAX_NO_OF_LCRS; i++) { |
|
293 @@ -1184,6 +1350,9 @@ |
|
294 char ruri[MAX_URI_SIZE]; |
|
295 unsigned int i, j, k, index, addr, port, strip, gw_index, |
|
296 duplicated_gw, flags, have_rpid_avp; |
|
297 + char *user; |
|
298 + char *realm; |
|
299 + char *passwd; |
|
300 uri_type scheme; |
|
301 uri_transport transport; |
|
302 struct ip_addr address; |
|
303 @@ -1407,6 +1576,9 @@ |
|
304 transport = (*gws)[index].transport; |
|
305 flags = (*gws)[index].flags; |
|
306 strip = (*gws)[index].strip; |
|
307 + user = (*gws)[index].user; |
|
308 + realm = (*gws)[index].realm; |
|
309 + passwd = (*gws)[index].passwd; |
|
310 if (strip > ruri_user.len) { |
|
311 LM_ERR("Strip count of gw is too large <%u>\n", strip); |
|
312 goto skip; |
|
313 @@ -1476,6 +1648,25 @@ |
|
314 val.s = value; |
|
315 add_avp(gw_uri_avp_type|AVP_VAL_STR, gw_uri_avp, val); |
|
316 LM_DBG("Added gw_uri_avp <%.*s>\n", value.len, value.s); |
|
317 + |
|
318 + value.s = user; |
|
319 + value.len = strlen(value.s); |
|
320 + val.s = value; |
|
321 + add_avp(user_avp_type|AVP_VAL_STR, user_avp, val); |
|
322 + LM_DBG("Added user_avp <%.*s>\n", value.len, value.s); |
|
323 + |
|
324 + value.s = realm; |
|
325 + value.len = strlen(value.s); |
|
326 + val.s = value; |
|
327 + add_avp(realm_avp_type|AVP_VAL_STR, realm_avp, val); |
|
328 + LM_DBG("Added realm_avp <%.*s>\n", value.len, value.s); |
|
329 + |
|
330 + value.s = passwd; |
|
331 + value.len = strlen(value.s); |
|
332 + val.s = value; |
|
333 + add_avp(passwd_avp_type|AVP_VAL_STR, passwd_avp, val); |
|
334 + LM_DBG("Added passwd_avp <%.*s>\n", value.len, value.s); |
|
335 + |
|
336 skip: |
|
337 continue; |
|
338 } |
|
339 @@ -1558,7 +1749,8 @@ |
|
340 static int next_gw(struct sip_msg* _m, char* _s1, char* _s2) |
|
341 { |
|
342 int_str gw_uri_val, ruri_user_val, val; |
|
343 - struct usr_avp *gu_avp, *ru_avp; |
|
344 + int_str user_val, realm_val, passwd_val; |
|
345 + struct usr_avp *gu_avp, *ru_avp, *usr_avp, *rlm_avp, *pwd_avp; |
|
346 int rval; |
|
347 str new_ruri; |
|
348 char *at, *at_char, *strip_char, *endptr; |
|
349 @@ -1575,6 +1767,35 @@ |
|
350 gw_uri_val.s.len = gw_uri_val.s.len - (at - gw_uri_val.s.s); |
|
351 gw_uri_val.s.s = at; |
|
352 |
|
353 + /* Save gateway AVPs for use in script */ |
|
354 + usr_avp = search_first_avp(user_avp_type, user_avp, &user_val, 0); |
|
355 + rlm_avp = search_first_avp(realm_avp_type, realm_avp, &realm_val, 0); |
|
356 + pwd_avp = search_first_avp(passwd_avp_type, passwd_avp, &passwd_val, 0); |
|
357 + if (!usr_avp) { |
|
358 + LM_DBG("User AVP no set\n"); |
|
359 + return -1; |
|
360 + } |
|
361 + else { |
|
362 + add_avp(user_avp_type|AVP_VAL_STR, user_avp, user_val); |
|
363 + LM_DBG("Added user_avp <%.*s>\n", user_val.s.len, user_val.s.s); |
|
364 + } |
|
365 + if (!rlm_avp) { |
|
366 + LM_DBG("Realm AVP no set\n"); |
|
367 + return -1; |
|
368 + } |
|
369 + else { |
|
370 + add_avp(realm_avp_type|AVP_VAL_STR, realm_avp, realm_val); |
|
371 + LM_DBG("Added realm_avp <%.*s>\n", realm_val.s.len, realm_val.s.s); |
|
372 + } |
|
373 + if (!pwd_avp) { |
|
374 + LM_DBG("Passwd AVP no set\n"); |
|
375 + return -1; |
|
376 + } |
|
377 + else { |
|
378 + add_avp(passwd_avp_type|AVP_VAL_STR, passwd_avp, passwd_val); |
|
379 + LM_DBG("Added passwd_avp <%.*s>\n", passwd_val.s.len, passwd_val.s.s); |
|
380 + } |
|
381 + |
|
382 /* Create new Request-URI taking URI user from ruri_user AVP |
|
383 and other parts of from gateway URI AVP. */ |
|
384 ru_avp = search_first_avp(ruri_user_avp_type, ruri_user_avp, |
|
385 Index: scripts/db_berkeley/opensips/gw |
|
386 diff -Nau scripts/db_berkeley/opensips/gw.orig scripts/db_berkeley/opensips/gw |
|
387 --- scripts/db_berkeley/opensips/gw.orig 2010-01-18 12:31:09.312068000 +0100 |
|
388 +++ scripts/db_berkeley/opensips/gw 2010-02-10 19:57:18.467214268 +0100 |
|
389 @@ -1,5 +1,5 @@ |
|
390 METADATA_COLUMNS |
|
391 -id(int) gw_name(str) grp_id(int) ip_addr(str) port(int) uri_scheme(int) transport(int) strip(int) tag(str) flags(int) |
|
392 +id(int) gw_name(str) grp_id(int) ip_addr(str) port(int) uri_scheme(int) transport(int) strip(int) tag(str) flags(int) user(str) realm(str) passwd(str) |
|
393 METADATA_KEY |
|
394 1 |
|
395 METADATA_READONLY |
|
396 @@ -7,4 +7,4 @@ |
|
397 METADATA_LOGFLAGS |
|
398 0 |
|
399 METADATA_DEFAULTS |
|
400 -NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NULL|0 |
|
401 +NIL|NIL|NIL|NIL|NIL|NIL|NIL|NIL|NULL|0|NULL|NULL|NULL |
|
402 Index: scripts/dbtext/opensips/gw |
|
403 diff -Nau scripts/dbtext/opensips/gw.orig scripts/dbtext/opensips/gw |
|
404 --- scripts/dbtext/opensips/gw.orig 2010-01-18 12:31:01.221183000 +0100 |
|
405 +++ scripts/dbtext/opensips/gw 2010-02-10 19:57:19.099512686 +0100 |
|
406 @@ -1 +1 @@ |
|
407 -id(int,auto) gw_name(string) grp_id(int) ip_addr(string) port(int,null) uri_scheme(int,null) transport(int,null) strip(int,null) tag(string,null) flags(int) |
|
408 +id(int,auto) gw_name(string) grp_id(int) ip_addr(string) port(int,null) uri_scheme(int,null) transport(int,null) strip(int,null) tag(string,null) flags(int) user(string,null) realm(string,null) passwd(string,null) |
|
409 Index: scripts/mysql/lcr-create.sql |
|
410 diff -Nau scripts/mysql/lcr-create.sql.orig scripts/mysql/lcr-create.sql |
|
411 --- scripts/mysql/lcr-create.sql.orig 2010-01-18 12:31:05.995635000 +0100 |
|
412 +++ scripts/mysql/lcr-create.sql 2010-02-10 20:00:24.123864285 +0100 |
|
413 @@ -10,6 +10,9 @@ |
|
414 strip TINYINT UNSIGNED, |
|
415 tag CHAR(16) DEFAULT NULL, |
|
416 flags INT UNSIGNED DEFAULT 0 NOT NULL, |
|
417 + user CHAR(16) DEFAULT NULL, |
|
418 + realm CHAR(16) DEFAULT NULL, |
|
419 + passwd CHAR(16) DEFAULT NULL, |
|
420 CONSTRAINT gw_name_idx UNIQUE (gw_name) |
|
421 ) ENGINE=MyISAM; |
|
422 |
|
423 Index: scripts/oracle/lcr-create.sql |
|
424 diff -Nau scripts/oracle/lcr-create.sql.orig scripts/oracle/lcr-create.sql |
|
425 --- scripts/oracle/lcr-create.sql.orig 2010-01-18 12:31:09.035730000 +0100 |
|
426 +++ scripts/oracle/lcr-create.sql 2010-02-10 20:00:23.643867960 +0100 |
|
427 @@ -10,6 +10,9 @@ |
|
428 strip NUMBER(5), |
|
429 tag VARCHAR2(16) DEFAULT NULL, |
|
430 flags NUMBER(10) DEFAULT 0 NOT NULL, |
|
431 + user VARCHAR2(16) DEFAULT NULL, |
|
432 + realm VARCHAR2(16) DEFAULT NULL, |
|
433 + passwd VARCHAR2(16) DEFAULT NULL, |
|
434 CONSTRAINT gw_gw_name_idx UNIQUE (gw_name) |
|
435 ); |
|
436 |
|
437 Index: scripts/postgres/lcr-create.sql |
|
438 diff -Nau scripts/postgres/lcr-create.sql.orig scripts/postgres/lcr-create.sql |
|
439 --- scripts/postgres/lcr-create.sql.orig 2010-01-18 12:31:09.151881000 +0100 |
|
440 +++ scripts/postgres/lcr-create.sql 2010-02-10 19:59:19.649196584 +0100 |
|
441 @@ -10,6 +10,9 @@ |
|
442 strip SMALLINT, |
|
443 tag VARCHAR(16) DEFAULT NULL, |
|
444 flags INTEGER DEFAULT 0 NOT NULL, |
|
445 + user VARCHAR(16) DEFAULT NULL, |
|
446 + realm VARCHAR(16) DEFAULT NULL, |
|
447 + passwd VARCHAR(16) DEFAULT NULL, |
|
448 CONSTRAINT gw_gw_name_idx UNIQUE (gw_name) |
|
449 ); |
|
450 |