mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-11-02 04:53:24 +00:00
Compare commits
596 Commits
openbsc/0.
...
on-waves/0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51a4bcc96a | ||
|
|
d6238120dd | ||
|
|
7407aec921 | ||
|
|
e575ce69ce | ||
|
|
c1ca0ff091 | ||
|
|
661e68b78f | ||
|
|
376e146cfb | ||
|
|
eb3ab2f85b | ||
|
|
ebc38e4f26 | ||
|
|
e2ab44a439 | ||
|
|
8b3cced773 | ||
|
|
3d1b0770f4 | ||
|
|
99743fb7ec | ||
|
|
a2a42a7561 | ||
|
|
ebd57da87d | ||
|
|
b0ee082bb0 | ||
|
|
81f6a4c0bf | ||
|
|
3978de52c1 | ||
|
|
7faf692cb7 | ||
|
|
0cf25d5154 | ||
|
|
08db178271 | ||
|
|
936d8c1b64 | ||
|
|
3170305e56 | ||
|
|
0f3490dd03 | ||
|
|
61e5e7bd8b | ||
|
|
f7b06fbe0c | ||
|
|
45403b1804 | ||
|
|
6782cea4bf | ||
|
|
ec7ecab66f | ||
|
|
d1287e379b | ||
|
|
3fb44f3e61 | ||
|
|
d48bfe0e93 | ||
|
|
41cdaf520d | ||
|
|
f94418a129 | ||
|
|
88b299ca24 | ||
|
|
58a2758e78 | ||
|
|
be3fdc2c6e | ||
|
|
6a8d765334 | ||
|
|
adebbfdfa7 | ||
|
|
afccfb9380 | ||
|
|
0f7a8258f0 | ||
|
|
2aa7f10939 | ||
|
|
37ba5b3e35 | ||
|
|
9f63d2b4ad | ||
|
|
d21b5de8c0 | ||
|
|
12b63716e2 | ||
|
|
184961ea3e | ||
|
|
a9ec86029f | ||
|
|
d1b19f3308 | ||
|
|
33f531fd12 | ||
|
|
b051b3b161 | ||
|
|
479a3aa707 | ||
|
|
fd2a877e25 | ||
|
|
53f797305f | ||
|
|
691b40e834 | ||
|
|
e511d54dd0 | ||
|
|
6edf7b9a51 | ||
|
|
e4045679a8 | ||
|
|
52ae9a461b | ||
|
|
5bd9493257 | ||
|
|
c92fd5d9d3 | ||
|
|
01cf14d679 | ||
|
|
840447e2bf | ||
|
|
3f7586d571 | ||
|
|
b74a9f13e5 | ||
|
|
bbc2c6e765 | ||
|
|
7e3724ad18 | ||
|
|
569dccf947 | ||
|
|
89a378e9aa | ||
|
|
4a78c7b250 | ||
|
|
c71013091a | ||
|
|
4b1cde10fe | ||
|
|
0f5a2345d1 | ||
|
|
ae81ff95ea | ||
|
|
e5981edf6a | ||
|
|
93cc16ae4f | ||
|
|
119a1976f5 | ||
|
|
c53c2ab524 | ||
|
|
32423500f6 | ||
|
|
c3a6a1dbe5 | ||
|
|
f4f090ee36 | ||
|
|
2a554bfcc4 | ||
|
|
a12dea66ca | ||
|
|
ec59bb04df | ||
|
|
4417f7f477 | ||
|
|
39563af27c | ||
|
|
242faaafd1 | ||
|
|
f77c0cd428 | ||
|
|
4103a3e5b9 | ||
|
|
4aca7f621f | ||
|
|
507d536ce8 | ||
|
|
cb618c7980 | ||
|
|
3c0702d3c5 | ||
|
|
caf24567d1 | ||
|
|
1d34c6ac5a | ||
|
|
1506f8e465 | ||
|
|
f044c585e2 | ||
|
|
6d17dd1314 | ||
|
|
38e9c82114 | ||
|
|
7cb6867ea3 | ||
|
|
d8138c43a1 | ||
|
|
46d9b94477 | ||
|
|
4f705b9f99 | ||
|
|
c592e697ce | ||
|
|
39608dc045 | ||
|
|
5fda90816f | ||
|
|
1803818092 | ||
|
|
439bb828f9 | ||
|
|
a06fea020d | ||
|
|
4511d891dd | ||
|
|
da0586a838 | ||
|
|
2c57232489 | ||
|
|
ad9f7830fb | ||
|
|
57ba7e3093 | ||
|
|
6ba3bcbbc6 | ||
|
|
ebb6b99c63 | ||
|
|
e08253a3f7 | ||
|
|
5e86095364 | ||
|
|
a7c144888d | ||
|
|
7897c4446b | ||
|
|
ff9e09b2bc | ||
|
|
ecf5cc294d | ||
|
|
82126763a7 | ||
|
|
a380c89a9c | ||
|
|
bedaf5da64 | ||
|
|
2b08aa35a6 | ||
|
|
c24632930a | ||
|
|
f140348eff | ||
|
|
b5de1b0781 | ||
|
|
b022cc3b8e | ||
|
|
e8396c9663 | ||
|
|
941839b300 | ||
|
|
23a0e46f11 | ||
|
|
cb8fd6e99e | ||
|
|
bb110f91e8 | ||
|
|
3ba36d5b57 | ||
|
|
bda581963d | ||
|
|
8d9833ef83 | ||
|
|
2ba40afc36 | ||
|
|
e66bea8ad7 | ||
|
|
e8a9f471ef | ||
|
|
e0ec326867 | ||
|
|
2d425059af | ||
|
|
135f797a37 | ||
|
|
f8eff2e4b5 | ||
|
|
70402a4e4d | ||
|
|
c2d66bdf5a | ||
|
|
80b584bbe7 | ||
|
|
15c21e8eec | ||
|
|
c0a1fff064 | ||
|
|
77fa4d2386 | ||
|
|
9be8752541 | ||
|
|
2b57b3cea4 | ||
|
|
00c531709a | ||
|
|
a094108f84 | ||
|
|
61e73eec3f | ||
|
|
1aa2798919 | ||
|
|
b829eac9bc | ||
|
|
7b1719327d | ||
|
|
493645eda9 | ||
|
|
8614cd0be7 | ||
|
|
19bd74d093 | ||
|
|
b54dda4cef | ||
|
|
b998d7b219 | ||
|
|
4821b5a847 | ||
|
|
84df56d577 | ||
|
|
4e23d5f87f | ||
|
|
4346424987 | ||
|
|
520656e004 | ||
|
|
9dac231fe4 | ||
|
|
2bb518a3bd | ||
|
|
476940f747 | ||
|
|
8deab8cdee | ||
|
|
a54f9e81c8 | ||
|
|
63cb447fd5 | ||
|
|
ed4390747f | ||
|
|
242d098d32 | ||
|
|
5eec9d91d8 | ||
|
|
63d18b51a7 | ||
|
|
74419497fc | ||
|
|
ccfd572647 | ||
|
|
07ba16fe03 | ||
|
|
e1ffc08f72 | ||
|
|
ef8117883b | ||
|
|
ae80f9291a | ||
|
|
1469600b0d | ||
|
|
c50b836540 | ||
|
|
754e801826 | ||
|
|
19722d4411 | ||
|
|
5615b982c2 | ||
|
|
aff596b8e1 | ||
|
|
f795164f04 | ||
|
|
b6c6d43daa | ||
|
|
82df124c8e | ||
|
|
189587f428 | ||
|
|
45ab581f37 | ||
|
|
f48776ea6a | ||
|
|
7fc17cff64 | ||
|
|
a5a7075fe5 | ||
|
|
2b4e366083 | ||
|
|
bf1eb64b02 | ||
|
|
f0fbae94ea | ||
|
|
8fe4df503c | ||
|
|
8da7103070 | ||
|
|
f73f6fad8c | ||
|
|
25cb84be12 | ||
|
|
d9ae25c1bf | ||
|
|
5c011366c9 | ||
|
|
79e2d4230d | ||
|
|
8ecd029b12 | ||
|
|
3c0508e94a | ||
|
|
f535aad612 | ||
|
|
d0ac8866f1 | ||
|
|
73f9a65f12 | ||
|
|
b2c55c49a8 | ||
|
|
8dc241959c | ||
|
|
f99709430a | ||
|
|
b9bc45b1b0 | ||
|
|
07d838a3bf | ||
|
|
65d10c1320 | ||
|
|
414ba77f75 | ||
|
|
59f2470650 | ||
|
|
339dfdb624 | ||
|
|
9e2e2e04d1 | ||
|
|
2ab6db0153 | ||
|
|
6cb97bdebe | ||
|
|
8c3694a282 | ||
|
|
191d23a889 | ||
|
|
d4e7888ae3 | ||
|
|
6c8c0ddbe2 | ||
|
|
19bab73d79 | ||
|
|
c19a65baae | ||
|
|
1ea8dbec90 | ||
|
|
500ff97c21 | ||
|
|
441273766a | ||
|
|
014136da47 | ||
|
|
91b5a31a2c | ||
|
|
575b89585f | ||
|
|
408cc4ace9 | ||
|
|
b4b135efbf | ||
|
|
54fa799129 | ||
|
|
a5963097ac | ||
|
|
221fb37518 | ||
|
|
4ec8a390cc | ||
|
|
cf3f1c8b3d | ||
|
|
984f3b8047 | ||
|
|
ec1f15d513 | ||
|
|
b76cd5ed7e | ||
|
|
1592550d98 | ||
|
|
5cdf42b1a4 | ||
|
|
3a6b1a41fb | ||
|
|
1b5b3bbfdb | ||
|
|
3a67035411 | ||
|
|
cb1937a4c5 | ||
|
|
3cfd5d6a02 | ||
|
|
6cc4dbfd46 | ||
|
|
9960d59fff | ||
|
|
161bd6d253 | ||
|
|
add3472e9f | ||
|
|
33b0bee457 | ||
|
|
6949db1bd8 | ||
|
|
8ae0080e21 | ||
|
|
546c296c4a | ||
|
|
86f42eb6a5 | ||
|
|
494c086dca | ||
|
|
6b18c8f3b6 | ||
|
|
3c71232b11 | ||
|
|
b4c7b274a1 | ||
|
|
7279d24232 | ||
|
|
87f6d26c2e | ||
|
|
fab2ff34c4 | ||
|
|
06d353e02e | ||
|
|
dfe47549c6 | ||
|
|
c70e8c2103 | ||
|
|
b462a03c35 | ||
|
|
6e0ec5b6fa | ||
|
|
6768387f16 | ||
|
|
5ef1234dd3 | ||
|
|
581e58d166 | ||
|
|
e308bb466a | ||
|
|
e4be5394ef | ||
|
|
81e1edd3e6 | ||
|
|
cfd1c28604 | ||
|
|
3ba8963a1d | ||
|
|
238d156481 | ||
|
|
516c4f073a | ||
|
|
fa22aa6bbd | ||
|
|
ef8253c495 | ||
|
|
6c0729fe70 | ||
|
|
e125d40f66 | ||
|
|
58df0ea9a0 | ||
|
|
8b120f0ef9 | ||
|
|
f2f1591ce7 | ||
|
|
f36a11a35d | ||
|
|
c77efdf057 | ||
|
|
b79994c952 | ||
|
|
616d222518 | ||
|
|
64e4e77558 | ||
|
|
9bdcc9ca75 | ||
|
|
e7d2ae69c9 | ||
|
|
52a66aa27e | ||
|
|
13d67b7ea3 | ||
|
|
b71517f07e | ||
|
|
4072ceed32 | ||
|
|
cf6bf63a0d | ||
|
|
88b614110f | ||
|
|
d9b825a5f5 | ||
|
|
b91e5f1da4 | ||
|
|
07bb509434 | ||
|
|
08db6ca509 | ||
|
|
6446ded81c | ||
|
|
7b8f6064d6 | ||
|
|
c6a1fe773d | ||
|
|
729d468fdf | ||
|
|
b37ce4c5a4 | ||
|
|
5cd62c0ba5 | ||
|
|
1e1acafafd | ||
|
|
fb83b7a86d | ||
|
|
ef0b641f63 | ||
|
|
27e0bfd3c7 | ||
|
|
bbfff6ec39 | ||
|
|
dc0914df09 | ||
|
|
0db691dcf6 | ||
|
|
bb45b73b20 | ||
|
|
5f5c1b6bcb | ||
|
|
e51cf4f946 | ||
|
|
749ba7f5ad | ||
|
|
860c8955c3 | ||
|
|
c33701c4e5 | ||
|
|
44d92b4728 | ||
|
|
8aaec620da | ||
|
|
a5a4014d67 | ||
|
|
9d519189ae | ||
|
|
f0fc618782 | ||
|
|
c57575bea8 | ||
|
|
8cdfe9fc37 | ||
|
|
0959f8cbe6 | ||
|
|
f21028985e | ||
|
|
483b768ab2 | ||
|
|
82cb311c4f | ||
|
|
2980442e33 | ||
|
|
fa7afb31e9 | ||
|
|
7513b3a1c2 | ||
|
|
135d99b36e | ||
|
|
5aaf7c164c | ||
|
|
790db1e01b | ||
|
|
81a8975662 | ||
|
|
fd876b7488 | ||
|
|
2ffe7aa340 | ||
|
|
538ea6d5c6 | ||
|
|
e14ec0dab4 | ||
|
|
8252b9b947 | ||
|
|
9fb88021dd | ||
|
|
b031d6ecae | ||
|
|
fcfdde5390 | ||
|
|
95e4d34f06 | ||
|
|
f5b6aa60ce | ||
|
|
c38b5884ff | ||
|
|
4079105a6c | ||
|
|
ceb072da34 | ||
|
|
b6e1a40c9c | ||
|
|
82ae7169a4 | ||
|
|
571ba8e4da | ||
|
|
bed6234e26 | ||
|
|
9d24578812 | ||
|
|
a087c4e75d | ||
|
|
6b64b26d8b | ||
|
|
22252a98e3 | ||
|
|
957bc93244 | ||
|
|
18bbe2e8a0 | ||
|
|
1b17913cbc | ||
|
|
ce2a36840d | ||
|
|
0e09feccb0 | ||
|
|
40a1de699a | ||
|
|
d906a366c8 | ||
|
|
d44d4c8c8b | ||
|
|
af0e1d7a85 | ||
|
|
d21b4d7f98 | ||
|
|
3bdaa69fb2 | ||
|
|
5c0132882a | ||
|
|
ed443e949e | ||
|
|
1df69f3c64 | ||
|
|
d7cafafeee | ||
|
|
e09348d366 | ||
|
|
5f1b7c14f5 | ||
|
|
5b3e9198f0 | ||
|
|
f0b21dfd25 | ||
|
|
acf8a0c59f | ||
|
|
e165d1aaa4 | ||
|
|
649496eb57 | ||
|
|
ca8d0063f9 | ||
|
|
c0d83b0647 | ||
|
|
4563eab30e | ||
|
|
332442d6c7 | ||
|
|
71ba85e4af | ||
|
|
dc5062b185 | ||
|
|
3ae2758fba | ||
|
|
faadfe2b93 | ||
|
|
d788f6688c | ||
|
|
4a2bb9e38b | ||
|
|
135a45c833 | ||
|
|
1a3d9dbabf | ||
|
|
a91d15df7e | ||
|
|
3368e2a3d1 | ||
|
|
929d788e21 | ||
|
|
99a263ff20 | ||
|
|
6958065f85 | ||
|
|
097c82b2bc | ||
|
|
abaeb3f55f | ||
|
|
f42e29c79c | ||
|
|
3177580cc1 | ||
|
|
cbe77e1657 | ||
|
|
3cedc4738f | ||
|
|
0834fd9b85 | ||
|
|
7b65c986eb | ||
|
|
17d751531e | ||
|
|
3c1221e2b2 | ||
|
|
92e9caed63 | ||
|
|
b464fb4a89 | ||
|
|
3f96458d73 | ||
|
|
d4c16b1080 | ||
|
|
18b63f4b41 | ||
|
|
ec32b5860e | ||
|
|
5179c8ef0b | ||
|
|
e95d4825f5 | ||
|
|
68884aa156 | ||
|
|
fe6bf777c3 | ||
|
|
facb5cdfc2 | ||
|
|
e95daf1925 | ||
|
|
52b4abdcb3 | ||
|
|
5e6466780f | ||
|
|
9eb6d88d64 | ||
|
|
aebea482f5 | ||
|
|
e9e190a8d8 | ||
|
|
b59f9350f8 | ||
|
|
92b1fe4c2d | ||
|
|
12f20d369c | ||
|
|
2008d3f54c | ||
|
|
bb7bc1155f | ||
|
|
009ad61dab | ||
|
|
829772d3c4 | ||
|
|
a26ebe40f5 | ||
|
|
a52f1cacb3 | ||
|
|
9cc020ab21 | ||
|
|
f5e71415a2 | ||
|
|
edee7944a5 | ||
|
|
82a8d6e393 | ||
|
|
1226c93937 | ||
|
|
b9c520f9b3 | ||
|
|
8a7ca57d3e | ||
|
|
f99f0930fd | ||
|
|
3e57388305 | ||
|
|
6552d0b596 | ||
|
|
a21a96f987 | ||
|
|
29f9f9fc79 | ||
|
|
b3121c5b3f | ||
|
|
f5284ae1cf | ||
|
|
d512e454b3 | ||
|
|
22481bf76d | ||
|
|
ff9d8b42e6 | ||
|
|
dc6af631e5 | ||
|
|
354ef81d80 | ||
|
|
b2eb83fa95 | ||
|
|
7f0f8b92ce | ||
|
|
bb60e225a1 | ||
|
|
4cd3d8a2c7 | ||
|
|
505117b778 | ||
|
|
5a29c7fa89 | ||
|
|
774f0723bf | ||
|
|
88c6eeaa7d | ||
|
|
b973955295 | ||
|
|
9d51a36528 | ||
|
|
ba3bbe55c1 | ||
|
|
0619478073 | ||
|
|
f8f184edab | ||
|
|
d838951302 | ||
|
|
f8e1b45a78 | ||
|
|
dd2c9fdbcf | ||
|
|
9991421cfb | ||
|
|
e30f0e1c75 | ||
|
|
18598ff66d | ||
|
|
8882c9e3a8 | ||
|
|
fdc64f6806 | ||
|
|
16b331d14f | ||
|
|
ab46372e2a | ||
|
|
be807e4250 | ||
|
|
71ddbf5c4f | ||
|
|
63bb29fac0 | ||
|
|
04b4f915a7 | ||
|
|
4d95ab2231 | ||
|
|
17944f7285 | ||
|
|
d2964b6cd1 | ||
|
|
1ce5d7c8b7 | ||
|
|
846457b10a | ||
|
|
e7b9771c4d | ||
|
|
d709900efa | ||
|
|
55b4f5cc2e | ||
|
|
1ac5ac75a9 | ||
|
|
5cf38ed1ab | ||
|
|
35d1531089 | ||
|
|
47e3777caa | ||
|
|
710f3c615c | ||
|
|
3111560e8a | ||
|
|
7396afbba4 | ||
|
|
52a72e217e | ||
|
|
ff0a562f9a | ||
|
|
556008d724 | ||
|
|
0094f84f30 | ||
|
|
86069143ff | ||
|
|
44f0be88a3 | ||
|
|
5d88b372d7 | ||
|
|
71c7bf5907 | ||
|
|
869033148c | ||
|
|
bc0f7c0988 | ||
|
|
7d06063cfb | ||
|
|
4e42b637fd | ||
|
|
f44de9942b | ||
|
|
3a110ae60b | ||
|
|
bb84adc465 | ||
|
|
8d123ea3c0 | ||
|
|
88ca894df7 | ||
|
|
42b0d6b494 | ||
|
|
82d8b0457b | ||
|
|
433d6ee1a2 | ||
|
|
203a6eddf8 | ||
|
|
56ef6249e3 | ||
|
|
b2a96b1be7 | ||
|
|
d4c29c1574 | ||
|
|
3d947e6d67 | ||
|
|
b62c9a19cf | ||
|
|
ff5957568f | ||
|
|
7d2e1ca4be | ||
|
|
7ce2e0c8b0 | ||
|
|
78d442420b | ||
|
|
8cd2709ebf | ||
|
|
41a1780102 | ||
|
|
2f84715984 | ||
|
|
7253154fc5 | ||
|
|
6c1c76683f | ||
|
|
a92fe9a4ca | ||
|
|
e83a3f584e | ||
|
|
118ddebc36 | ||
|
|
bb53004d47 | ||
|
|
6af20842cb | ||
|
|
cc41cb07e7 | ||
|
|
d6fb23523a | ||
|
|
2aa0b45cc0 | ||
|
|
619df61ad2 | ||
|
|
893ea65f38 | ||
|
|
64b811f113 | ||
|
|
91fc9bf862 | ||
|
|
111a58dd37 | ||
|
|
d1a2563a74 | ||
|
|
7d3ef919ce | ||
|
|
cba98d87d6 | ||
|
|
5c18ad0829 | ||
|
|
0d9ed87d5c | ||
|
|
ec7be0c969 | ||
|
|
9be3347601 | ||
|
|
3eef7b7d81 | ||
|
|
9de4a6daa9 | ||
|
|
851ace9f33 | ||
|
|
d1dd069b48 | ||
|
|
401db32ca2 | ||
|
|
17e03d21d2 | ||
|
|
26a9bff201 | ||
|
|
80fb260a60 | ||
|
|
55a0716da7 | ||
|
|
c88fb75616 | ||
|
|
d55a4dc326 | ||
|
|
a4e6f2e6e1 | ||
|
|
7f71d99cc3 | ||
|
|
b92167cf80 | ||
|
|
4b6a6dbe7e | ||
|
|
763e8c7766 | ||
|
|
823ff16088 | ||
|
|
6f93c6a1e0 | ||
|
|
f97e48b0de | ||
|
|
761600b0fd | ||
|
|
8549462bc6 | ||
|
|
436e5c6308 | ||
|
|
f8b9d844c1 | ||
|
|
58ec07d580 | ||
|
|
71465c21f4 | ||
|
|
16d0a833f8 | ||
|
|
ea72b62cac | ||
|
|
49a84ec6e9 | ||
|
|
42c636b6c8 | ||
|
|
a0a55f555e | ||
|
|
23ed00e410 | ||
|
|
3fe910b9f1 | ||
|
|
097bdeb77d | ||
|
|
1b85de02e0 | ||
|
|
2281d1835f | ||
|
|
fb4433a129 | ||
|
|
d954dcf9e1 |
3
libosmocore/.gitignore
vendored
3
libosmocore/.gitignore
vendored
@@ -20,3 +20,6 @@ ltmain.sh
|
||||
install-sh
|
||||
stamp-h1
|
||||
libtool
|
||||
|
||||
.tarball-version
|
||||
.version
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
SUBDIRS = include src tests
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libosmocore.pc
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
$(top_srcdir)/.version:
|
||||
echo $(VERSION) > $@-t && mv $@-t $@
|
||||
dist-hook:
|
||||
echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
AC_INIT
|
||||
AC_INIT([libosmocore],
|
||||
m4_esyscmd([./git-version-gen .tarball-version]),
|
||||
[openbsc-devel@lists.openbsc.org])
|
||||
|
||||
AM_INIT_AUTOMAKE(libosmocore, 0.0alpha1)
|
||||
AM_INIT_AUTOMAKE([dist-bzip2])
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
@@ -9,9 +11,11 @@ dnl checks for programs
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PROG_CC
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_RANLIB
|
||||
LT_INIT
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
dnl checks for header files
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS(execinfo.h sys/select.h)
|
||||
|
||||
151
libosmocore/git-version-gen
Executable file
151
libosmocore/git-version-gen
Executable file
@@ -0,0 +1,151 @@
|
||||
#!/bin/sh
|
||||
# Print a version string.
|
||||
scriptversion=2010-01-28.01
|
||||
|
||||
# Copyright (C) 2007-2010 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
|
||||
# It may be run two ways:
|
||||
# - from a git repository in which the "git describe" command below
|
||||
# produces useful output (thus requiring at least one signed tag)
|
||||
# - from a non-git-repo directory containing a .tarball-version file, which
|
||||
# presumes this script is invoked like "./git-version-gen .tarball-version".
|
||||
|
||||
# In order to use intra-version strings in your project, you will need two
|
||||
# separate generated version string files:
|
||||
#
|
||||
# .tarball-version - present only in a distribution tarball, and not in
|
||||
# a checked-out repository. Created with contents that were learned at
|
||||
# the last time autoconf was run, and used by git-version-gen. Must not
|
||||
# be present in either $(srcdir) or $(builddir) for git-version-gen to
|
||||
# give accurate answers during normal development with a checked out tree,
|
||||
# but must be present in a tarball when there is no version control system.
|
||||
# Therefore, it cannot be used in any dependencies. GNUmakefile has
|
||||
# hooks to force a reconfigure at distribution time to get the value
|
||||
# correct, without penalizing normal development with extra reconfigures.
|
||||
#
|
||||
# .version - present in a checked-out repository and in a distribution
|
||||
# tarball. Usable in dependencies, particularly for files that don't
|
||||
# want to depend on config.h but do want to track version changes.
|
||||
# Delete this file prior to any autoconf run where you want to rebuild
|
||||
# files to pick up a version string change; and leave it stale to
|
||||
# minimize rebuild time after unrelated changes to configure sources.
|
||||
#
|
||||
# It is probably wise to add these two files to .gitignore, so that you
|
||||
# don't accidentally commit either generated file.
|
||||
#
|
||||
# Use the following line in your configure.ac, so that $(VERSION) will
|
||||
# automatically be up-to-date each time configure is run (and note that
|
||||
# since configure.ac no longer includes a version string, Makefile rules
|
||||
# should not depend on configure.ac for version updates).
|
||||
#
|
||||
# AC_INIT([GNU project],
|
||||
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
|
||||
# [bug-project@example])
|
||||
#
|
||||
# Then use the following lines in your Makefile.am, so that .version
|
||||
# will be present for dependencies, and so that .tarball-version will
|
||||
# exist in distribution tarballs.
|
||||
#
|
||||
# BUILT_SOURCES = $(top_srcdir)/.version
|
||||
# $(top_srcdir)/.version:
|
||||
# echo $(VERSION) > $@-t && mv $@-t $@
|
||||
# dist-hook:
|
||||
# echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
case $# in
|
||||
1) ;;
|
||||
*) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
|
||||
esac
|
||||
|
||||
tarball_version_file=$1
|
||||
nl='
|
||||
'
|
||||
|
||||
# First see if there is a tarball-only version file.
|
||||
# then try "git describe", then default.
|
||||
if test -f $tarball_version_file
|
||||
then
|
||||
v=`cat $tarball_version_file` || exit 1
|
||||
case $v in
|
||||
*$nl*) v= ;; # reject multi-line output
|
||||
[0-9]*) ;;
|
||||
*) v= ;;
|
||||
esac
|
||||
test -z "$v" \
|
||||
&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
|
||||
fi
|
||||
|
||||
if test -n "$v"
|
||||
then
|
||||
: # use $v
|
||||
elif
|
||||
v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
||||
&& case $v in
|
||||
[0-9]*) ;;
|
||||
v[0-9]*) ;;
|
||||
*) (exit 1) ;;
|
||||
esac
|
||||
then
|
||||
# Is this a new git that lists number of commits since the last
|
||||
# tag or the previous older version that did not?
|
||||
# Newer: v6.10-77-g0f8faeb
|
||||
# Older: v6.10-g0f8faeb
|
||||
case $v in
|
||||
*-*-*) : git describe is okay three part flavor ;;
|
||||
*-*)
|
||||
: git describe is older two part flavor
|
||||
# Recreate the number of commits and rewrite such that the
|
||||
# result is the same as if we were using the newer version
|
||||
# of git describe.
|
||||
vtag=`echo "$v" | sed 's/-.*//'`
|
||||
numcommits=`git rev-list "$vtag"..HEAD | wc -l`
|
||||
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
|
||||
;;
|
||||
esac
|
||||
|
||||
# Change the first '-' to a '.', so version-comparing tools work properly.
|
||||
# Remove the "g" in git describe's output string, to save a byte.
|
||||
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
|
||||
else
|
||||
v=UNKNOWN
|
||||
fi
|
||||
|
||||
v=`echo "$v" |sed 's/^v//'`
|
||||
|
||||
# Don't declare a version "dirty" merely because a time stamp has changed.
|
||||
git status > /dev/null 2>&1
|
||||
|
||||
dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
|
||||
case "$dirty" in
|
||||
'') ;;
|
||||
*) # Append the suffix only if there isn't one already.
|
||||
case $v in
|
||||
*-dirty) ;;
|
||||
*) v="$v-dirty" ;;
|
||||
esac ;;
|
||||
esac
|
||||
|
||||
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
|
||||
echo "$v" | tr -d '\012'
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
||||
@@ -1,7 +1,7 @@
|
||||
osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \
|
||||
tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \
|
||||
gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \
|
||||
gsm48_ie.h
|
||||
gsm48_ie.h logging.h
|
||||
|
||||
if ENABLE_TALLOC
|
||||
osmocore_HEADERS += talloc.h
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#include <osmocore/gsm48_ie.h>
|
||||
|
||||
extern const struct tlv_definition gsm48_att_tlvdef;
|
||||
extern const char *cc_state_names[32];
|
||||
extern const char *gsm48_cc_msg_names[0x40];
|
||||
const char *gsm48_cc_state_name(uint8_t state);
|
||||
const char *gsm48_cc_msg_name(uint8_t msgtype);
|
||||
const char *rr_cause_name(uint8_t cause);
|
||||
|
||||
void gsm48_generate_lai(struct gsm48_loc_area_id *lai48, uint16_t mcc,
|
||||
|
||||
130
libosmocore/include/osmocore/logging.h
Normal file
130
libosmocore/include/osmocore/logging.h
Normal file
@@ -0,0 +1,130 @@
|
||||
#ifndef _OSMOCORE_LOGGING_H
|
||||
#define _OSMOCORE_LOGGING_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <osmocore/linuxlist.h>
|
||||
|
||||
#define LOG_MAX_CATEGORY 32
|
||||
#define LOG_MAX_CTX 8
|
||||
#define LOG_MAX_FILTERS 8
|
||||
|
||||
#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEBUGP(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 0, fmt, ## args)
|
||||
#define DEBUGPC(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 1, fmt, ## args)
|
||||
#else
|
||||
#define DEBUGP(xss, fmt, args...)
|
||||
#define DEBUGPC(ss, fmt, args...)
|
||||
#endif
|
||||
|
||||
#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
|
||||
|
||||
char *hexdump(const unsigned char *buf, int len);
|
||||
void logp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
|
||||
|
||||
/* new logging interface */
|
||||
#define LOGP(ss, level, fmt, args...) \
|
||||
logp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
|
||||
#define LOGPC(ss, level, fmt, args...) \
|
||||
logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)
|
||||
|
||||
/* different levels */
|
||||
#define LOGL_DEBUG 1 /* debugging information */
|
||||
#define LOGL_INFO 3
|
||||
#define LOGL_NOTICE 5 /* abnormal/unexpected condition */
|
||||
#define LOGL_ERROR 7 /* error condition, requires user action */
|
||||
#define LOGL_FATAL 8 /* fatal, program aborted */
|
||||
|
||||
#define LOG_FILTER_ALL 0x0001
|
||||
|
||||
struct log_category {
|
||||
uint8_t loglevel;
|
||||
uint8_t enabled;
|
||||
};
|
||||
|
||||
struct log_info_cat {
|
||||
const char *name;
|
||||
const char *color;
|
||||
const char *description;
|
||||
uint8_t loglevel;
|
||||
uint8_t enabled;
|
||||
};
|
||||
|
||||
/* log context information, passed to filter */
|
||||
struct log_context {
|
||||
void *ctx[LOG_MAX_CTX+1];
|
||||
};
|
||||
|
||||
struct log_target;
|
||||
|
||||
typedef int log_filter(const struct log_context *ctx,
|
||||
struct log_target *target);
|
||||
|
||||
struct log_info {
|
||||
/* filter callback function */
|
||||
log_filter *filter_fn;
|
||||
|
||||
/* per-category information */
|
||||
const struct log_info_cat *cat;
|
||||
unsigned int num_cat;
|
||||
};
|
||||
|
||||
struct log_target {
|
||||
struct llist_head entry;
|
||||
|
||||
int filter_map;
|
||||
void *filter_data[LOG_MAX_FILTERS+1];
|
||||
|
||||
struct log_category categories[LOG_MAX_CATEGORY+1];
|
||||
uint8_t loglevel;
|
||||
int use_color:1;
|
||||
int print_timestamp:1;
|
||||
|
||||
union {
|
||||
struct {
|
||||
FILE *out;
|
||||
} tgt_stdout;
|
||||
|
||||
struct {
|
||||
int priority;
|
||||
} tgt_syslog;
|
||||
|
||||
struct {
|
||||
void *vty;
|
||||
} tgt_vty;
|
||||
};
|
||||
|
||||
void (*output) (struct log_target *target, const char *string);
|
||||
};
|
||||
|
||||
/* use the above macros */
|
||||
void logp2(unsigned int subsys, unsigned int level, char *file,
|
||||
int line, int cont, const char *format, ...)
|
||||
__attribute__ ((format (printf, 6, 7)));
|
||||
void log_init(const struct log_info *cat);
|
||||
|
||||
/* context management */
|
||||
void log_reset_context(void);
|
||||
int log_set_context(uint8_t ctx, void *value);
|
||||
|
||||
/* filter on the targets */
|
||||
void log_set_all_filter(struct log_target *target, int);
|
||||
|
||||
void log_set_use_color(struct log_target *target, int);
|
||||
void log_set_print_timestamp(struct log_target *target, int);
|
||||
void log_set_log_level(struct log_target *target, int log_level);
|
||||
void log_parse_category_mask(struct log_target *target, const char* mask);
|
||||
int log_parse_level(const char *lvl);
|
||||
int log_parse_category(const char *category);
|
||||
void log_set_category_filter(struct log_target *target, int category,
|
||||
int enable, int level);
|
||||
|
||||
/* management of the targets */
|
||||
struct log_target *log_target_create(void);
|
||||
struct log_target *log_target_create_stderr(void);
|
||||
void log_add_target(struct log_target *target);
|
||||
void log_del_target(struct log_target *target);
|
||||
|
||||
#endif /* _OSMOCORE_LOGGING_H */
|
||||
@@ -16,9 +16,8 @@ uint8_t rsl_enc_chan_nr(uint8_t type, uint8_t subch, uint8_t timeslot);
|
||||
/* decode channel number as per Section 9.3.1 */
|
||||
int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *timeslot);
|
||||
|
||||
extern const struct value_string rsl_rlm_cause_strs[];
|
||||
|
||||
const char *rsl_err_name(uint8_t err);
|
||||
const char *rsl_rlm_cause_name(uint8_t err);
|
||||
|
||||
/* Section 3.3.2.3 TS 05.02. I think this looks like a table */
|
||||
int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf);
|
||||
|
||||
@@ -38,6 +38,7 @@ struct write_queue {
|
||||
};
|
||||
|
||||
void write_queue_init(struct write_queue *queue, int max_length);
|
||||
void write_queue_clear(struct write_queue *queue);
|
||||
int write_queue_enqueue(struct write_queue *queue, struct msgb *data);
|
||||
int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what);
|
||||
|
||||
|
||||
1
libosmocore/m4/DUMMY
Normal file
1
libosmocore/m4/DUMMY
Normal file
@@ -0,0 +1 @@
|
||||
Dummply placeholder.
|
||||
@@ -9,7 +9,8 @@ lib_LTLIBRARIES = libosmocore.la
|
||||
|
||||
libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c \
|
||||
tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \
|
||||
write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c
|
||||
write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c \
|
||||
logging.c
|
||||
|
||||
if ENABLE_TALLOC
|
||||
libosmocore_la_SOURCES += talloc.c
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* GSM Mobile Radio Interface Layer 3 messages
|
||||
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
|
||||
|
||||
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
|
||||
/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
*
|
||||
* All Rights Reserved
|
||||
@@ -75,27 +75,29 @@ const struct tlv_definition gsm48_att_tlvdef = {
|
||||
},
|
||||
};
|
||||
|
||||
static const char *rr_cause_names[] = {
|
||||
[GSM48_RR_CAUSE_NORMAL] = "Normal event",
|
||||
[GSM48_RR_CAUSE_ABNORMAL_UNSPEC] = "Abnormal release, unspecified",
|
||||
[GSM48_RR_CAUSE_ABNORMAL_UNACCT] = "Abnormal release, channel unacceptable",
|
||||
[GSM48_RR_CAUSE_ABNORMAL_TIMER] = "Abnormal release, timer expired",
|
||||
[GSM48_RR_CAUSE_ABNORMAL_NOACT] = "Abnormal release, no activity on radio path",
|
||||
[GSM48_RR_CAUSE_PREMPTIVE_REL] = "Preemptive release",
|
||||
[GSM48_RR_CAUSE_HNDOVER_IMP] = "Handover impossible, timing advance out of range",
|
||||
[GSM48_RR_CAUSE_CHAN_MODE_UNACCT] = "Channel mode unacceptable",
|
||||
[GSM48_RR_CAUSE_FREQ_NOT_IMPL] = "Frequency not implemented",
|
||||
[GSM48_RR_CAUSE_CALL_CLEARED] = "Call already cleared",
|
||||
[GSM48_RR_CAUSE_SEMANT_INCORR] = "Semantically incorrect message",
|
||||
[GSM48_RR_CAUSE_INVALID_MAND_INF] = "Invalid mandatory information",
|
||||
[GSM48_RR_CAUSE_MSG_TYPE_N] = "Message type non-existant or not implemented",
|
||||
[GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT] = "Message type not compatible with protocol state",
|
||||
[GSM48_RR_CAUSE_COND_IE_ERROR] = "Conditional IE error",
|
||||
[GSM48_RR_CAUSE_NO_CELL_ALLOC_A] = "No cell allocation available",
|
||||
[GSM48_RR_CAUSE_PROT_ERROR_UNSPC] = "Protocol error unspecified",
|
||||
static const struct value_string rr_cause_names[] = {
|
||||
{ GSM48_RR_CAUSE_NORMAL, "Normal event" },
|
||||
{ GSM48_RR_CAUSE_ABNORMAL_UNSPEC, "Abnormal release, unspecified" },
|
||||
{ GSM48_RR_CAUSE_ABNORMAL_UNACCT, "Abnormal release, channel unacceptable" },
|
||||
{ GSM48_RR_CAUSE_ABNORMAL_TIMER, "Abnormal release, timer expired" },
|
||||
{ GSM48_RR_CAUSE_ABNORMAL_NOACT, "Abnormal release, no activity on radio path" },
|
||||
{ GSM48_RR_CAUSE_PREMPTIVE_REL, "Preemptive release" },
|
||||
{ GSM48_RR_CAUSE_HNDOVER_IMP, "Handover impossible, timing advance out of range" },
|
||||
{ GSM48_RR_CAUSE_CHAN_MODE_UNACCT, "Channel mode unacceptable" },
|
||||
{ GSM48_RR_CAUSE_FREQ_NOT_IMPL, "Frequency not implemented" },
|
||||
{ GSM48_RR_CAUSE_CALL_CLEARED, "Call already cleared" },
|
||||
{ GSM48_RR_CAUSE_SEMANT_INCORR, "Semantically incorrect message" },
|
||||
{ GSM48_RR_CAUSE_INVALID_MAND_INF, "Invalid mandatory information" },
|
||||
{ GSM48_RR_CAUSE_MSG_TYPE_N, "Message type non-existant or not implemented" },
|
||||
{ GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT, "Message type not compatible with protocol state" },
|
||||
{ GSM48_RR_CAUSE_COND_IE_ERROR, "Conditional IE error" },
|
||||
{ GSM48_RR_CAUSE_NO_CELL_ALLOC_A, "No cell allocation available" },
|
||||
{ GSM48_RR_CAUSE_PROT_ERROR_UNSPC, "Protocol error unspecified" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
const char *cc_state_names[32] = {
|
||||
/* FIXME: convert to value_string */
|
||||
static const char *cc_state_names[32] = {
|
||||
"NULL",
|
||||
"INITIATED",
|
||||
"illegal state 2",
|
||||
@@ -130,83 +132,61 @@ const char *cc_state_names[32] = {
|
||||
"illegal state 31",
|
||||
};
|
||||
|
||||
const char *gsm48_cc_msg_names[0x40] = {
|
||||
"unknown 0x00",
|
||||
"ALERTING",
|
||||
"CALL_PROC",
|
||||
"PROGRESS",
|
||||
"ESTAB",
|
||||
"SETUP",
|
||||
"ESTAB_CONF",
|
||||
"CONNECT",
|
||||
"CALL_CONF",
|
||||
"START_CC",
|
||||
"unknown 0x0a",
|
||||
"RECALL",
|
||||
"unknown 0x0c",
|
||||
"unknown 0x0d",
|
||||
"EMERG_SETUP",
|
||||
"CONNECT_ACK",
|
||||
"USER_INFO",
|
||||
"unknown 0x11",
|
||||
"unknown 0x12",
|
||||
"MODIFY_REJECT",
|
||||
"unknown 0x14",
|
||||
"unknown 0x15",
|
||||
"unknown 0x16",
|
||||
"MODIFY",
|
||||
"HOLD",
|
||||
"HOLD_ACK",
|
||||
"HOLD_REJ",
|
||||
"unknown 0x1b",
|
||||
"RETR",
|
||||
"RETR_ACK",
|
||||
"RETR_REJ",
|
||||
"MODIFY_COMPL",
|
||||
"unknown 0x20",
|
||||
"unknown 0x21",
|
||||
"unknown 0x22",
|
||||
"unknown 0x23",
|
||||
"unknown 0x24",
|
||||
"DISCONNECT",
|
||||
"unknown 0x26",
|
||||
"unknown 0x27",
|
||||
"unknown 0x28",
|
||||
"unknown 0x29",
|
||||
"RELEASE_COMPL",
|
||||
"unknown 0x2b",
|
||||
"unknown 0x2c",
|
||||
"RELEASE",
|
||||
"unknown 0x2e",
|
||||
"unknown 0x2f",
|
||||
"unknown 0x30",
|
||||
"STOP_DTMF",
|
||||
"STOP_DTMF_ACK",
|
||||
"unknown 0x33",
|
||||
"STATUS_ENQ",
|
||||
"START_DTMF",
|
||||
"START_DTMF_ACK",
|
||||
"START_DTMF_REJ",
|
||||
"unknown 0x38",
|
||||
"CONG_CTRL",
|
||||
"FACILITY",
|
||||
"unknown 0x3b",
|
||||
"STATUS",
|
||||
"unknown 0x3d",
|
||||
"NOTIFY",
|
||||
"unknown 0x3f",
|
||||
const char *gsm48_cc_state_name(uint8_t state)
|
||||
{
|
||||
if (state < ARRAY_SIZE(cc_state_names))
|
||||
return cc_state_names[state];
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
|
||||
static const struct value_string cc_msg_names[] = {
|
||||
{ GSM48_MT_CC_ALERTING, "ALERTING" },
|
||||
{ GSM48_MT_CC_CALL_PROC, "CALL_PROC" },
|
||||
{ GSM48_MT_CC_PROGRESS, "PROGRESS" },
|
||||
{ GSM48_MT_CC_ESTAB, "ESTAB" },
|
||||
{ GSM48_MT_CC_SETUP, "SETUP" },
|
||||
{ GSM48_MT_CC_ESTAB_CONF, "ESTAB_CONF" },
|
||||
{ GSM48_MT_CC_CONNECT, "CONNECT" },
|
||||
{ GSM48_MT_CC_CALL_CONF, "CALL_CONF" },
|
||||
{ GSM48_MT_CC_START_CC, "START_CC" },
|
||||
{ GSM48_MT_CC_RECALL, "RECALL" },
|
||||
{ GSM48_MT_CC_EMERG_SETUP, "EMERG_SETUP" },
|
||||
{ GSM48_MT_CC_CONNECT_ACK, "CONNECT_ACK" },
|
||||
{ GSM48_MT_CC_USER_INFO, "USER_INFO" },
|
||||
{ GSM48_MT_CC_MODIFY_REJECT, "MODIFY_REJECT" },
|
||||
{ GSM48_MT_CC_MODIFY, "MODIFY" },
|
||||
{ GSM48_MT_CC_HOLD, "HOLD" },
|
||||
{ GSM48_MT_CC_HOLD_ACK, "HOLD_ACK" },
|
||||
{ GSM48_MT_CC_HOLD_REJ, "HOLD_REJ" },
|
||||
{ GSM48_MT_CC_RETR, "RETR" },
|
||||
{ GSM48_MT_CC_RETR_ACK, "RETR_ACK" },
|
||||
{ GSM48_MT_CC_RETR_REJ, "RETR_REJ" },
|
||||
{ GSM48_MT_CC_MODIFY_COMPL, "MODIFY_COMPL" },
|
||||
{ GSM48_MT_CC_DISCONNECT, "DISCONNECT" },
|
||||
{ GSM48_MT_CC_RELEASE_COMPL, "RELEASE_COMPL" },
|
||||
{ GSM48_MT_CC_RELEASE, "RELEASE" },
|
||||
{ GSM48_MT_CC_STOP_DTMF, "STOP_DTMF" },
|
||||
{ GSM48_MT_CC_STOP_DTMF_ACK, "STOP_DTMF_ACK" },
|
||||
{ GSM48_MT_CC_STATUS_ENQ, "STATUS_ENQ" },
|
||||
{ GSM48_MT_CC_START_DTMF, "START_DTMF" },
|
||||
{ GSM48_MT_CC_START_DTMF_ACK, "START_DTMF_ACK" },
|
||||
{ GSM48_MT_CC_START_DTMF_REJ, "START_DTMF_REJ" },
|
||||
{ GSM48_MT_CC_CONG_CTRL, "CONG_CTRL" },
|
||||
{ GSM48_MT_CC_FACILITY, "FACILITY" },
|
||||
{ GSM48_MT_CC_STATUS, "STATUS" },
|
||||
{ GSM48_MT_CC_NOTIFY, "NOTFIY" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static char strbuf[64];
|
||||
const char *gsm48_cc_msg_name(uint8_t msgtype)
|
||||
{
|
||||
return get_value_string(cc_msg_names, msgtype);
|
||||
}
|
||||
|
||||
const char *rr_cause_name(uint8_t cause)
|
||||
{
|
||||
if (cause < ARRAY_SIZE(rr_cause_names) &&
|
||||
rr_cause_names[cause])
|
||||
return rr_cause_names[cause];
|
||||
|
||||
snprintf(strbuf, sizeof(strbuf), "0x%02x", cause);
|
||||
return strbuf;
|
||||
return get_value_string(rr_cause_names, cause);
|
||||
}
|
||||
|
||||
static void to_bcd(uint8_t *bcd, uint16_t val)
|
||||
|
||||
345
libosmocore/src/logging.c
Normal file
345
libosmocore/src/logging.c
Normal file
@@ -0,0 +1,345 @@
|
||||
/* Debugging/Logging support code */
|
||||
|
||||
/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <osmocore/talloc.h>
|
||||
#include <osmocore/utils.h>
|
||||
#include <osmocore/logging.h>
|
||||
|
||||
static const struct log_info *log_info;
|
||||
|
||||
static struct log_context log_context;
|
||||
static void *tall_log_ctx = NULL;
|
||||
static LLIST_HEAD(target_list);
|
||||
|
||||
static const struct value_string loglevel_strs[] = {
|
||||
{ 0, "EVERYTHING" },
|
||||
{ LOGL_DEBUG, "DEBUG" },
|
||||
{ LOGL_INFO, "INFO" },
|
||||
{ LOGL_NOTICE, "NOTICE" },
|
||||
{ LOGL_ERROR, "ERROR" },
|
||||
{ LOGL_FATAL, "FATAL" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
int log_parse_level(const char *lvl)
|
||||
{
|
||||
return get_string_value(loglevel_strs, lvl);
|
||||
}
|
||||
|
||||
int log_parse_category(const char *category)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < log_info->num_cat; ++i) {
|
||||
if (!strcasecmp(log_info->cat[i].name+1, category))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the category mask.
|
||||
* The format can be this: category1:category2:category3
|
||||
* or category1,2:category2,3:...
|
||||
*/
|
||||
void log_parse_category_mask(struct log_target* target, const char *_mask)
|
||||
{
|
||||
int i = 0;
|
||||
char *mask = strdup(_mask);
|
||||
char *category_token = NULL;
|
||||
|
||||
/* Disable everything to enable it afterwards */
|
||||
for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
|
||||
target->categories[i].enabled = 0;
|
||||
|
||||
category_token = strtok(mask, ":");
|
||||
do {
|
||||
for (i = 0; i < log_info->num_cat; ++i) {
|
||||
char* colon = strstr(category_token, ",");
|
||||
int length = strlen(category_token);
|
||||
|
||||
if (colon)
|
||||
length = colon - category_token;
|
||||
|
||||
if (strncasecmp(log_info->cat[i].name, category_token,
|
||||
length) == 0) {
|
||||
int level = 0;
|
||||
|
||||
if (colon)
|
||||
level = atoi(colon+1);
|
||||
|
||||
target->categories[i].enabled = 1;
|
||||
target->categories[i].loglevel = level;
|
||||
}
|
||||
}
|
||||
} while ((category_token = strtok(NULL, ":")));
|
||||
|
||||
free(mask);
|
||||
}
|
||||
|
||||
static const char* color(int subsys)
|
||||
{
|
||||
if (subsys < log_info->num_cat)
|
||||
return log_info->cat[subsys].color;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _output(struct log_target *target, unsigned int subsys,
|
||||
char *file, int line, int cont, const char *format,
|
||||
va_list ap)
|
||||
{
|
||||
char col[30];
|
||||
char sub[30];
|
||||
char tim[30];
|
||||
char buf[4096];
|
||||
char final[4096];
|
||||
|
||||
/* prepare the data */
|
||||
col[0] = '\0';
|
||||
sub[0] = '\0';
|
||||
tim[0] = '\0';
|
||||
buf[0] = '\0';
|
||||
|
||||
/* are we using color */
|
||||
if (target->use_color) {
|
||||
const char *c = color(subsys);
|
||||
if (c) {
|
||||
snprintf(col, sizeof(col), "%s", color(subsys));
|
||||
col[sizeof(col)-1] = '\0';
|
||||
}
|
||||
}
|
||||
vsnprintf(buf, sizeof(buf), format, ap);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
if (!cont) {
|
||||
if (target->print_timestamp) {
|
||||
char *timestr;
|
||||
time_t tm;
|
||||
tm = time(NULL);
|
||||
timestr = ctime(&tm);
|
||||
timestr[strlen(timestr)-1] = '\0';
|
||||
snprintf(tim, sizeof(tim), "%s ", timestr);
|
||||
tim[sizeof(tim)-1] = '\0';
|
||||
}
|
||||
snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
|
||||
sub[sizeof(sub)-1] = '\0';
|
||||
}
|
||||
|
||||
snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
|
||||
final[sizeof(final)-1] = '\0';
|
||||
target->output(target, final);
|
||||
}
|
||||
|
||||
|
||||
static void _logp(unsigned int subsys, int level, char *file, int line,
|
||||
int cont, const char *format, va_list ap)
|
||||
{
|
||||
struct log_target *tar;
|
||||
|
||||
llist_for_each_entry(tar, &target_list, entry) {
|
||||
struct log_category *category;
|
||||
int output = 0;
|
||||
|
||||
category = &tar->categories[subsys];
|
||||
/* subsystem is not supposed to be logged */
|
||||
if (!category->enabled)
|
||||
continue;
|
||||
|
||||
/* Check the global log level */
|
||||
if (tar->loglevel != 0 && level < tar->loglevel)
|
||||
continue;
|
||||
|
||||
/* Check the category log level */
|
||||
if (tar->loglevel == 0 && category->loglevel != 0 &&
|
||||
level < category->loglevel)
|
||||
continue;
|
||||
|
||||
/* Apply filters here... if that becomes messy we will
|
||||
* need to put filters in a list and each filter will
|
||||
* say stop, continue, output */
|
||||
if ((tar->filter_map & LOG_FILTER_ALL) != 0)
|
||||
output = 1;
|
||||
else if (log_info->filter_fn)
|
||||
output = log_info->filter_fn(&log_context,
|
||||
tar);
|
||||
|
||||
if (output) {
|
||||
/* FIXME: copying the va_list is an ugly
|
||||
* workaround against a bug hidden somewhere in
|
||||
* _output. If we do not copy here, the first
|
||||
* call to _output() will corrupt the va_list
|
||||
* contents, and any further _output() calls
|
||||
* with the same va_list will segfault */
|
||||
va_list bp;
|
||||
va_copy(bp, ap);
|
||||
_output(tar, subsys, file, line, cont, format, bp);
|
||||
va_end(bp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void logp(unsigned int subsys, char *file, int line, int cont,
|
||||
const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
_logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
_logp(subsys, level, file, line, cont, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static char hexd_buff[4096];
|
||||
|
||||
char *hexdump(const unsigned char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
char *cur = hexd_buff;
|
||||
|
||||
hexd_buff[0] = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
|
||||
int rc = snprintf(cur, len_remain, "%02x ", buf[i]);
|
||||
if (rc <= 0)
|
||||
break;
|
||||
cur += rc;
|
||||
}
|
||||
hexd_buff[sizeof(hexd_buff)-1] = 0;
|
||||
return hexd_buff;
|
||||
}
|
||||
|
||||
void log_add_target(struct log_target *target)
|
||||
{
|
||||
llist_add_tail(&target->entry, &target_list);
|
||||
}
|
||||
|
||||
void log_del_target(struct log_target *target)
|
||||
{
|
||||
llist_del(&target->entry);
|
||||
}
|
||||
|
||||
void log_reset_context(void)
|
||||
{
|
||||
memset(&log_context, 0, sizeof(log_context));
|
||||
}
|
||||
|
||||
int log_set_context(uint8_t ctx_nr, void *value)
|
||||
{
|
||||
if (ctx_nr > LOG_MAX_CTX)
|
||||
return -EINVAL;
|
||||
|
||||
log_context.ctx[ctx_nr] = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void log_set_all_filter(struct log_target *target, int all)
|
||||
{
|
||||
if (all)
|
||||
target->filter_map |= LOG_FILTER_ALL;
|
||||
else
|
||||
target->filter_map &= ~LOG_FILTER_ALL;
|
||||
}
|
||||
|
||||
void log_set_use_color(struct log_target *target, int use_color)
|
||||
{
|
||||
target->use_color = use_color;
|
||||
}
|
||||
|
||||
void log_set_print_timestamp(struct log_target *target, int print_timestamp)
|
||||
{
|
||||
target->print_timestamp = print_timestamp;
|
||||
}
|
||||
|
||||
void log_set_log_level(struct log_target *target, int log_level)
|
||||
{
|
||||
target->loglevel = log_level;
|
||||
}
|
||||
|
||||
void log_set_category_filter(struct log_target *target, int category,
|
||||
int enable, int level)
|
||||
{
|
||||
if (category >= log_info->num_cat)
|
||||
return;
|
||||
target->categories[category].enabled = !!enable;
|
||||
target->categories[category].loglevel = level;
|
||||
}
|
||||
|
||||
static void _stderr_output(struct log_target *target, const char *log)
|
||||
{
|
||||
fprintf(target->tgt_stdout.out, "%s", log);
|
||||
fflush(target->tgt_stdout.out);
|
||||
}
|
||||
|
||||
struct log_target *log_target_create(void)
|
||||
{
|
||||
struct log_target *target;
|
||||
|
||||
target = talloc_zero(tall_log_ctx, struct log_target);
|
||||
if (!target)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&target->entry);
|
||||
memcpy(target->categories, log_info->cat,
|
||||
sizeof(struct log_category)*log_info->num_cat);
|
||||
target->use_color = 1;
|
||||
target->print_timestamp = 0;
|
||||
target->loglevel = 0;
|
||||
return target;
|
||||
}
|
||||
|
||||
struct log_target *log_target_create_stderr(void)
|
||||
{
|
||||
struct log_target *target;
|
||||
|
||||
target = log_target_create();
|
||||
if (!target)
|
||||
return NULL;
|
||||
|
||||
target->tgt_stdout.out = stderr;
|
||||
target->output = _stderr_output;
|
||||
return target;
|
||||
}
|
||||
|
||||
void log_init(const struct log_info *cat)
|
||||
{
|
||||
tall_log_ctx = talloc_named_const(NULL, 1, "logging");
|
||||
log_info = cat;
|
||||
}
|
||||
@@ -176,42 +176,47 @@ int rsl_dec_chan_nr(uint8_t chan_nr, uint8_t *type, uint8_t *subch, uint8_t *tim
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: convert to value_string */
|
||||
static const char *rsl_err_vals[0xff] = {
|
||||
[RSL_ERR_RADIO_IF_FAIL] = "Radio Interface Failure",
|
||||
[RSL_ERR_RADIO_LINK_FAIL] = "Radio Link Failure",
|
||||
[RSL_ERR_HANDOVER_ACC_FAIL] = "Handover Access Failure",
|
||||
[RSL_ERR_TALKER_ACC_FAIL] = "Talker Access Failure",
|
||||
[RSL_ERR_OM_INTERVENTION] = "O&M Intervention",
|
||||
[RSL_ERR_NORMAL_UNSPEC] = "Normal event, unspecified",
|
||||
[RSL_ERR_T_MSRFPCI_EXP] = "Siemens: T_MSRFPCI Expired",
|
||||
[RSL_ERR_EQUIPMENT_FAIL] = "Equipment Failure",
|
||||
[RSL_ERR_RR_UNAVAIL] = "Radio Resource not available",
|
||||
[RSL_ERR_TERR_CH_FAIL] = "Terrestrial Channel Failure",
|
||||
[RSL_ERR_CCCH_OVERLOAD] = "CCCH Overload",
|
||||
[RSL_ERR_ACCH_OVERLOAD] = "ACCH Overload",
|
||||
[RSL_ERR_PROCESSOR_OVERLOAD] = "Processor Overload",
|
||||
[RSL_ERR_RES_UNAVAIL] = "Resource not available, unspecified",
|
||||
[RSL_ERR_TRANSC_UNAVAIL] = "Transcoding not available",
|
||||
[RSL_ERR_SERV_OPT_UNAVAIL] = "Service or Option not available",
|
||||
[RSL_ERR_ENCR_UNIMPL] = "Encryption algorithm not implemented",
|
||||
[RSL_ERR_SERV_OPT_UNIMPL] = "Service or Option not implemented",
|
||||
[RSL_ERR_RCH_ALR_ACTV_ALLOC] = "Radio channel already activated",
|
||||
[RSL_ERR_INVALID_MESSAGE] = "Invalid Message, unspecified",
|
||||
[RSL_ERR_MSG_DISCR] = "Message Discriminator Error",
|
||||
[RSL_ERR_MSG_TYPE] = "Message Type Error",
|
||||
[RSL_ERR_MSG_SEQ] = "Message Sequence Error",
|
||||
[RSL_ERR_IE_ERROR] = "General IE error",
|
||||
[RSL_ERR_MAND_IE_ERROR] = "Mandatory IE error",
|
||||
[RSL_ERR_OPT_IE_ERROR] = "Optional IE error",
|
||||
[RSL_ERR_IE_NONEXIST] = "IE non-existent",
|
||||
[RSL_ERR_IE_LENGTH] = "IE length error",
|
||||
[RSL_ERR_IE_CONTENT] = "IE content error",
|
||||
[RSL_ERR_PROTO] = "Protocol error, unspecified",
|
||||
[RSL_ERR_INTERWORKING] = "Interworking error, unspecified",
|
||||
static const struct value_string rsl_err_vals[] = {
|
||||
{ RSL_ERR_RADIO_IF_FAIL, "Radio Interface Failure" },
|
||||
{ RSL_ERR_RADIO_LINK_FAIL, "Radio Link Failure" },
|
||||
{ RSL_ERR_HANDOVER_ACC_FAIL, "Handover Access Failure" },
|
||||
{ RSL_ERR_TALKER_ACC_FAIL, "Talker Access Failure" },
|
||||
{ RSL_ERR_OM_INTERVENTION, "O&M Intervention" },
|
||||
{ RSL_ERR_NORMAL_UNSPEC, "Normal event, unspecified" },
|
||||
{ RSL_ERR_T_MSRFPCI_EXP, "Siemens: T_MSRFPCI Expired" },
|
||||
{ RSL_ERR_EQUIPMENT_FAIL, "Equipment Failure" },
|
||||
{ RSL_ERR_RR_UNAVAIL, "Radio Resource not available" },
|
||||
{ RSL_ERR_TERR_CH_FAIL, "Terrestrial Channel Failure" },
|
||||
{ RSL_ERR_CCCH_OVERLOAD, "CCCH Overload" },
|
||||
{ RSL_ERR_ACCH_OVERLOAD, "ACCH Overload" },
|
||||
{ RSL_ERR_PROCESSOR_OVERLOAD, "Processor Overload" },
|
||||
{ RSL_ERR_RES_UNAVAIL, "Resource not available, unspecified" },
|
||||
{ RSL_ERR_TRANSC_UNAVAIL, "Transcoding not available" },
|
||||
{ RSL_ERR_SERV_OPT_UNAVAIL, "Service or Option not available" },
|
||||
{ RSL_ERR_ENCR_UNIMPL, "Encryption algorithm not implemented" },
|
||||
{ RSL_ERR_SERV_OPT_UNIMPL, "Service or Option not implemented" },
|
||||
{ RSL_ERR_RCH_ALR_ACTV_ALLOC, "Radio channel already activated" },
|
||||
{ RSL_ERR_INVALID_MESSAGE, "Invalid Message, unspecified" },
|
||||
{ RSL_ERR_MSG_DISCR, "Message Discriminator Error" },
|
||||
{ RSL_ERR_MSG_TYPE, "Message Type Error" },
|
||||
{ RSL_ERR_MSG_SEQ, "Message Sequence Error" },
|
||||
{ RSL_ERR_IE_ERROR, "General IE error" },
|
||||
{ RSL_ERR_MAND_IE_ERROR, "Mandatory IE error" },
|
||||
{ RSL_ERR_OPT_IE_ERROR, "Optional IE error" },
|
||||
{ RSL_ERR_IE_NONEXIST, "IE non-existent" },
|
||||
{ RSL_ERR_IE_LENGTH, "IE length error" },
|
||||
{ RSL_ERR_IE_CONTENT, "IE content error" },
|
||||
{ RSL_ERR_PROTO, "Protocol error, unspecified" },
|
||||
{ RSL_ERR_INTERWORKING, "Interworking error, unspecified" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const struct value_string rsl_rlm_cause_strs[] = {
|
||||
const char *rsl_err_name(uint8_t err)
|
||||
{
|
||||
return get_value_string(rsl_err_vals, err);
|
||||
}
|
||||
|
||||
static const struct value_string rsl_rlm_cause_strs[] = {
|
||||
{ RLL_CAUSE_T200_EXPIRED, "Timer T200 expired (N200+1) times" },
|
||||
{ RLL_CAUSE_REEST_REQ, "Re-establishment request" },
|
||||
{ RLL_CAUSE_UNSOL_UA_RESP, "Unsolicited UA response" },
|
||||
@@ -229,12 +234,9 @@ const struct value_string rsl_rlm_cause_strs[] = {
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
const char *rsl_err_name(uint8_t err)
|
||||
const char *rsl_rlm_cause_name(uint8_t err)
|
||||
{
|
||||
if (rsl_err_vals[err])
|
||||
return rsl_err_vals[err];
|
||||
else
|
||||
return "unknown";
|
||||
return get_value_string(rsl_rlm_cause_strs, err);
|
||||
}
|
||||
|
||||
/* Section 3.3.2.3 TS 05.02. I think this looks like a table */
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <osmocore/utils.h>
|
||||
|
||||
static char namebuf[255];
|
||||
const char *get_value_string(const struct value_string *vs, uint32_t val)
|
||||
{
|
||||
int i;
|
||||
@@ -15,7 +17,9 @@ const char *get_value_string(const struct value_string *vs, uint32_t val)
|
||||
if (vs[i].value == val)
|
||||
return vs[i].str;
|
||||
}
|
||||
return "unknown";
|
||||
|
||||
snprintf(namebuf, sizeof(namebuf), "unknown 0x%x", val);
|
||||
return namebuf;
|
||||
}
|
||||
|
||||
int get_string_value(const struct value_string *vs, const char *str)
|
||||
|
||||
@@ -72,3 +72,14 @@ int write_queue_enqueue(struct write_queue *queue, struct msgb *data)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void write_queue_clear(struct write_queue *queue)
|
||||
{
|
||||
while (!llist_empty(&queue->msg_queue)) {
|
||||
struct msgb *msg = msgb_dequeue(&queue->msg_queue);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
queue->current_length = 0;
|
||||
queue->bfd.when &= ~BSC_FD_WRITE;
|
||||
}
|
||||
|
||||
3
openbsc/.gitignore
vendored
3
openbsc/.gitignore
vendored
@@ -23,6 +23,9 @@ install-sh
|
||||
missing
|
||||
stamp-h1
|
||||
|
||||
# git-version-gen magic
|
||||
.tarball-version
|
||||
.version
|
||||
|
||||
|
||||
# apps and app data
|
||||
|
||||
@@ -6,5 +6,8 @@ SUBDIRS = include src tests
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = openbsc.pc libsccp.pc
|
||||
|
||||
#dist-hook:
|
||||
# rm -rf `find $(distdir) -name .svn`
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
$(top_srcdir)/.version:
|
||||
echo $(VERSION) > $@-t && mv $@-t $@
|
||||
dist-hook:
|
||||
echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
dnl Process this file with autoconf to produce a configure script
|
||||
AC_INIT
|
||||
|
||||
AM_INIT_AUTOMAKE(openbsc, 0.0alpha1)
|
||||
AC_INIT(openbsc, 0.3.99.9onwaves)
|
||||
AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
|
||||
|
||||
dnl kernel style compile messages
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
@@ -16,7 +15,7 @@ dnl checks for libraries
|
||||
AC_SEARCH_LIBS(crypt, crypt,
|
||||
[LIBCRYPT="-lcrypt"; AC_DEFINE([VTY_CRYPT_PW], [], [Use crypt functionality of vty.])])
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore)
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.1.3)
|
||||
|
||||
dnl checks for header files
|
||||
AC_HEADER_STDC
|
||||
@@ -52,4 +51,5 @@ AC_OUTPUT(
|
||||
tests/db/Makefile
|
||||
tests/channel/Makefile
|
||||
tests/sccp/Makefile
|
||||
tests/bsc-nat/Makefile
|
||||
Makefile)
|
||||
|
||||
2
openbsc/contrib/README
Normal file
2
openbsc/contrib/README
Normal file
@@ -0,0 +1,2 @@
|
||||
This contains a set of scripts used for the development of the
|
||||
MSC functionality.
|
||||
@@ -10,7 +10,7 @@ rsip_resp = """200 321321332\r\n"""
|
||||
audit_packet = """AUEP %d 13@mgw MGCP 1.0\r\n"""
|
||||
crcx_packet = """CRCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n"""
|
||||
dlcx_packet = """DLCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\n"""
|
||||
mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 4400 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"""
|
||||
mdcx_packet = """MDCX %d 14@mgw MGCP 1.0\r\nC: 4a84ad5d25f\r\nI: %d\r\nL: p:20, a:GSM-EFR, nt:IN\r\nM: recvonly\r\n\r\nv=0\r\no=- 258696477 0 IN IP4 172.16.1.107\r\ns=-\r\nc=IN IP4 172.16.1.107\r\nt=0 0\r\nm=audio 6666 RTP/AVP 127\r\na=rtpmap:127 GSM-EFR/8000/1\r\na=ptime:20\r\na=recvonly\r\nm=image 4402 udptl t38\r\na=T38FaxVersion:0\r\na=T38MaxBitRate:14400\r\n"""
|
||||
|
||||
def hexdump(src, length=8):
|
||||
"""Recipe is from http://code.activestate.com/recipes/142812/"""
|
||||
@@ -25,15 +25,24 @@ def hexdump(src, length=8):
|
||||
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
server_socket.bind(("127.0.0.1", MGCP_CALLAGENT_PORT))
|
||||
server_socket.setblocking(0)
|
||||
server_socket.setblocking(1)
|
||||
|
||||
|
||||
def send_receive(packet):
|
||||
last_ci = 1
|
||||
def send_and_receive(packet):
|
||||
global last_ci
|
||||
server_socket.sendto(packet, ("127.0.0.1", MGCP_GATEWAY_PORT))
|
||||
try:
|
||||
data, addr = server_socket.recvfrom(4096)
|
||||
|
||||
# attempt to store the CI of the response
|
||||
list = data.split("\n")
|
||||
for item in list:
|
||||
if item.startswith("I: "):
|
||||
last_ci = int(item[3:])
|
||||
|
||||
print hexdump(data), addr
|
||||
except socket.error:
|
||||
except socket.error, e:
|
||||
print e
|
||||
pass
|
||||
|
||||
def generate_tid():
|
||||
@@ -42,13 +51,10 @@ def generate_tid():
|
||||
|
||||
|
||||
|
||||
i = 1
|
||||
while True:
|
||||
send_receive(rsip_resp)
|
||||
send_receive(audit_packet)
|
||||
send_receive(crcx_packet % generate_tid() )
|
||||
send_receive(mdcx_packet % (generate_tid(), i))
|
||||
send_receive(dlcx_packet % (generate_tid(), i))
|
||||
i = i + 1
|
||||
send_and_receive(audit_packet % generate_tid())
|
||||
send_and_receive(crcx_packet % generate_tid() )
|
||||
send_and_receive(mdcx_packet % (generate_tid(), last_ci))
|
||||
send_and_receive(dlcx_packet % (generate_tid(), last_ci))
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
30
openbsc/contrib/send_handshake.py
Executable file
30
openbsc/contrib/send_handshake.py
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
|
||||
# packages
|
||||
ACK ="\x00\x01\xfe\x06"
|
||||
RESET_ACK = "\x00\x13\xfd\x09\x00\x03\x07\x0b\x04\x43\x01\x00\xfe\x04\x43\x5c\x00\xfe\x03\x00\x01\x31"
|
||||
PAGE = "\x00\x20\xfd\x09\x00\x03\x07\x0b\x04\x43\x01\x00\xfe\x04\x43\x5c\x00\xfe\x10\x00\x0e\x52\x08\x08\x29\x42\x08\x05\x03\x12\x23\x42\x1a\x01\x06"
|
||||
|
||||
|
||||
# simple handshake...
|
||||
sys.stdout.write(ACK)
|
||||
sys.stdout.flush()
|
||||
sys.stdin.read(4)
|
||||
|
||||
# wait for some data and send reset ack
|
||||
sys.stdin.read(21)
|
||||
sys.stdout.write(RESET_ACK)
|
||||
sys.stdout.flush()
|
||||
|
||||
sys.stdout.write(RESET_ACK)
|
||||
sys.stdout.flush()
|
||||
|
||||
# page a subscriber
|
||||
sys.stdout.write(PAGE)
|
||||
sys.stdout.flush()
|
||||
|
||||
while True:
|
||||
sys.stdin.read(1)
|
||||
|
||||
151
openbsc/git-version-gen
Executable file
151
openbsc/git-version-gen
Executable file
@@ -0,0 +1,151 @@
|
||||
#!/bin/sh
|
||||
# Print a version string.
|
||||
scriptversion=2010-01-28.01
|
||||
|
||||
# Copyright (C) 2007-2010 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
|
||||
# It may be run two ways:
|
||||
# - from a git repository in which the "git describe" command below
|
||||
# produces useful output (thus requiring at least one signed tag)
|
||||
# - from a non-git-repo directory containing a .tarball-version file, which
|
||||
# presumes this script is invoked like "./git-version-gen .tarball-version".
|
||||
|
||||
# In order to use intra-version strings in your project, you will need two
|
||||
# separate generated version string files:
|
||||
#
|
||||
# .tarball-version - present only in a distribution tarball, and not in
|
||||
# a checked-out repository. Created with contents that were learned at
|
||||
# the last time autoconf was run, and used by git-version-gen. Must not
|
||||
# be present in either $(srcdir) or $(builddir) for git-version-gen to
|
||||
# give accurate answers during normal development with a checked out tree,
|
||||
# but must be present in a tarball when there is no version control system.
|
||||
# Therefore, it cannot be used in any dependencies. GNUmakefile has
|
||||
# hooks to force a reconfigure at distribution time to get the value
|
||||
# correct, without penalizing normal development with extra reconfigures.
|
||||
#
|
||||
# .version - present in a checked-out repository and in a distribution
|
||||
# tarball. Usable in dependencies, particularly for files that don't
|
||||
# want to depend on config.h but do want to track version changes.
|
||||
# Delete this file prior to any autoconf run where you want to rebuild
|
||||
# files to pick up a version string change; and leave it stale to
|
||||
# minimize rebuild time after unrelated changes to configure sources.
|
||||
#
|
||||
# It is probably wise to add these two files to .gitignore, so that you
|
||||
# don't accidentally commit either generated file.
|
||||
#
|
||||
# Use the following line in your configure.ac, so that $(VERSION) will
|
||||
# automatically be up-to-date each time configure is run (and note that
|
||||
# since configure.ac no longer includes a version string, Makefile rules
|
||||
# should not depend on configure.ac for version updates).
|
||||
#
|
||||
# AC_INIT([GNU project],
|
||||
# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
|
||||
# [bug-project@example])
|
||||
#
|
||||
# Then use the following lines in your Makefile.am, so that .version
|
||||
# will be present for dependencies, and so that .tarball-version will
|
||||
# exist in distribution tarballs.
|
||||
#
|
||||
# BUILT_SOURCES = $(top_srcdir)/.version
|
||||
# $(top_srcdir)/.version:
|
||||
# echo $(VERSION) > $@-t && mv $@-t $@
|
||||
# dist-hook:
|
||||
# echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
case $# in
|
||||
1) ;;
|
||||
*) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
|
||||
esac
|
||||
|
||||
tarball_version_file=$1
|
||||
nl='
|
||||
'
|
||||
|
||||
# First see if there is a tarball-only version file.
|
||||
# then try "git describe", then default.
|
||||
if test -f $tarball_version_file
|
||||
then
|
||||
v=`cat $tarball_version_file` || exit 1
|
||||
case $v in
|
||||
*$nl*) v= ;; # reject multi-line output
|
||||
[0-9]*) ;;
|
||||
*) v= ;;
|
||||
esac
|
||||
test -z "$v" \
|
||||
&& echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
|
||||
fi
|
||||
|
||||
if test -n "$v"
|
||||
then
|
||||
: # use $v
|
||||
elif test -d ./../.git \
|
||||
&& v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
|
||||
|| git describe --abbrev=4 HEAD 2>/dev/null` \
|
||||
&& case $v in
|
||||
[0-9]*) ;;
|
||||
v[0-9]*) ;;
|
||||
*) (exit 1) ;;
|
||||
esac
|
||||
then
|
||||
# Is this a new git that lists number of commits since the last
|
||||
# tag or the previous older version that did not?
|
||||
# Newer: v6.10-77-g0f8faeb
|
||||
# Older: v6.10-g0f8faeb
|
||||
case $v in
|
||||
*-*-*) : git describe is okay three part flavor ;;
|
||||
*-*)
|
||||
: git describe is older two part flavor
|
||||
# Recreate the number of commits and rewrite such that the
|
||||
# result is the same as if we were using the newer version
|
||||
# of git describe.
|
||||
vtag=`echo "$v" | sed 's/-.*//'`
|
||||
numcommits=`git rev-list "$vtag"..HEAD | wc -l`
|
||||
v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
|
||||
;;
|
||||
esac
|
||||
|
||||
# Change the first '-' to a '.', so version-comparing tools work properly.
|
||||
# Remove the "g" in git describe's output string, to save a byte.
|
||||
v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
|
||||
else
|
||||
v=UNKNOWN
|
||||
fi
|
||||
|
||||
v=`echo "$v" |sed 's/^v//'`
|
||||
|
||||
# Don't declare a version "dirty" merely because a time stamp has changed.
|
||||
git status > /dev/null 2>&1
|
||||
|
||||
dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
|
||||
case "$dirty" in
|
||||
'') ;;
|
||||
*) # Append the suffix only if there isn't one already.
|
||||
case $v in
|
||||
*-dirty) ;;
|
||||
*) v="$v-dirty" ;;
|
||||
esac ;;
|
||||
esac
|
||||
|
||||
# Omit the trailing newline, so that m4_esyscmd can use the result directly.
|
||||
echo "$v" | tr -d '\012'
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-end: "$"
|
||||
# End:
|
||||
@@ -5,7 +5,8 @@ noinst_HEADERS = abis_nm.h abis_rsl.h db.h gsm_04_08.h gsm_data.h \
|
||||
ipaccess.h rs232.h openbscdefines.h rtp_proxy.h \
|
||||
bsc_rll.h mncc.h transaction.h ussd.h gsm_04_80.h \
|
||||
silent_call.h mgcp.h meas_rep.h rest_octets.h \
|
||||
system_information.h handover.h mgcp_internal.h
|
||||
system_information.h handover.h mgcp_internal.h \
|
||||
vty.h bssap.h bsc_msc.h bsc_nat.h bsc_msc_rf.h
|
||||
|
||||
openbsc_HEADERS = gsm_04_08.h meas_rep.h
|
||||
openbsc_HEADERS = gsm_04_08.h meas_rep.h bsc_api.h
|
||||
openbscdir = $(includedir)/openbsc
|
||||
|
||||
@@ -59,7 +59,7 @@ int rsl_ipacc_crcx(struct gsm_lchan *lchan);
|
||||
int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip,
|
||||
u_int16_t port, u_int8_t rtp_payload2);
|
||||
int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan);
|
||||
int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan);
|
||||
int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan, int act);
|
||||
|
||||
int abis_rsl_rcvmsg(struct msgb *msg);
|
||||
|
||||
@@ -68,12 +68,16 @@ unsigned int get_paging_group(u_int64_t imsi, unsigned int bs_cc_chans,
|
||||
unsigned int n_pag_blocks(int bs_ccch_sdcch_comb, unsigned int bs_ag_blks_res);
|
||||
u_int64_t str_to_imsi(const char *imsi_str);
|
||||
u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan);
|
||||
int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id);
|
||||
int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id, u_int8_t release_reason);
|
||||
|
||||
int rsl_lchan_set_state(struct gsm_lchan *lchan, int);
|
||||
|
||||
int rsl_lchan_set_state(struct gsm_lchan *lchan, int);
|
||||
|
||||
/* to be provided by external code */
|
||||
int abis_rsl_sendmsg(struct msgb *msg);
|
||||
int rsl_deact_sacch(struct gsm_lchan *lchan);
|
||||
int rsl_chan_release(struct gsm_lchan *lchan);
|
||||
int rsl_lchan_rll_release(struct gsm_lchan *lchan, u_int8_t link_id);
|
||||
|
||||
/* BCCH related code */
|
||||
int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf);
|
||||
|
||||
6
openbsc/include/openbsc/bsc_api.h
Normal file
6
openbsc/include/openbsc/bsc_api.h
Normal file
@@ -0,0 +1,6 @@
|
||||
/* GSM 08.08 like API for OpenBSC */
|
||||
|
||||
#include "gsm_data.h"
|
||||
|
||||
|
||||
int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id);
|
||||
48
openbsc/include/openbsc/bsc_msc.h
Normal file
48
openbsc/include/openbsc/bsc_msc.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/* Routines to talk to the MSC using the IPA Protocol */
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BSC_MSC_H
|
||||
#define BSC_MSC_H
|
||||
|
||||
#include <osmocore/write_queue.h>
|
||||
#include <osmocore/timer.h>
|
||||
|
||||
struct bsc_msc_connection {
|
||||
struct write_queue write_queue;
|
||||
int is_connected;
|
||||
int is_authenticated;
|
||||
const char *ip;
|
||||
int port;
|
||||
|
||||
void (*connection_loss) (struct bsc_msc_connection *);
|
||||
void (*connected) (struct bsc_msc_connection *);
|
||||
struct timer_list reconnect_timer;
|
||||
struct timer_list timeout_timer;
|
||||
};
|
||||
|
||||
struct bsc_msc_connection *bsc_msc_create(const char *ip, int port);
|
||||
int bsc_msc_connect(struct bsc_msc_connection *);
|
||||
void bsc_msc_schedule_connect(struct bsc_msc_connection *);
|
||||
|
||||
void bsc_msc_lost(struct bsc_msc_connection *);
|
||||
|
||||
#endif
|
||||
20
openbsc/include/openbsc/bsc_msc_rf.h
Normal file
20
openbsc/include/openbsc/bsc_msc_rf.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef BSC_MSC_RF
|
||||
#define BSC_MSC_RF
|
||||
|
||||
#include <osmocore/write_queue.h>
|
||||
|
||||
struct gsm_network;
|
||||
|
||||
struct bsc_msc_rf {
|
||||
struct bsc_fd listen;
|
||||
struct gsm_network *gsm_network;
|
||||
};
|
||||
|
||||
struct bsc_msc_rf_conn {
|
||||
struct write_queue queue;
|
||||
struct gsm_network *gsm_network;
|
||||
};
|
||||
|
||||
struct bsc_msc_rf *bsc_msc_rf_create(const char *path, struct gsm_network *net);
|
||||
|
||||
#endif
|
||||
287
openbsc/include/openbsc/bsc_nat.h
Normal file
287
openbsc/include/openbsc/bsc_nat.h
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BSC_NAT_H
|
||||
#define BSC_NAT_H
|
||||
|
||||
#include "mgcp.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sccp/sccp_types.h>
|
||||
|
||||
#include <osmocore/select.h>
|
||||
#include <osmocore/msgb.h>
|
||||
#include <osmocore/timer.h>
|
||||
#include <osmocore/write_queue.h>
|
||||
#include <osmocore/statistics.h>
|
||||
|
||||
#include <regex.h>
|
||||
|
||||
#define DIR_BSC 1
|
||||
#define DIR_MSC 2
|
||||
|
||||
#define NAT_IPAC_PROTO_MGCP 0xfc
|
||||
|
||||
struct bsc_nat;
|
||||
|
||||
/*
|
||||
* For the NAT we will need to analyze and later patch
|
||||
* the received message. This would require us to parse
|
||||
* the IPA and SCCP header twice. Instead of doing this
|
||||
* we will have one analyze structure and have the patching
|
||||
* and filter operate on the same structure.
|
||||
*/
|
||||
struct bsc_nat_parsed {
|
||||
/* ip access prototype */
|
||||
int ipa_proto;
|
||||
|
||||
/* source local reference */
|
||||
struct sccp_source_reference *src_local_ref;
|
||||
|
||||
/* destination local reference */
|
||||
struct sccp_source_reference *dest_local_ref;
|
||||
|
||||
/* called ssn number */
|
||||
int called_ssn;
|
||||
|
||||
/* calling ssn number */
|
||||
int calling_ssn;
|
||||
|
||||
/* sccp message type */
|
||||
int sccp_type;
|
||||
|
||||
/* bssap type, e.g. 0 for BSS Management */
|
||||
int bssap;
|
||||
|
||||
/* the gsm0808 message type */
|
||||
int gsm_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* Per BSC data structure
|
||||
*/
|
||||
struct bsc_connection {
|
||||
struct llist_head list_entry;
|
||||
|
||||
/* do we know anything about this BSC? */
|
||||
int authenticated;
|
||||
|
||||
/* the fd we use to communicate */
|
||||
struct write_queue write_queue;
|
||||
|
||||
/* the BSS associated */
|
||||
struct bsc_config *cfg;
|
||||
|
||||
/* a timeout node */
|
||||
struct timer_list id_timeout;
|
||||
|
||||
/* pong timeout */
|
||||
struct timer_list ping_timeout;
|
||||
struct timer_list pong_timeout;
|
||||
|
||||
/* a back pointer */
|
||||
struct bsc_nat *nat;
|
||||
};
|
||||
|
||||
/*
|
||||
* Per SCCP source local reference patch table. It needs to
|
||||
* be updated on new SCCP connections, connection confirm and reject,
|
||||
* and on the loss of the BSC connection.
|
||||
*/
|
||||
struct sccp_connections {
|
||||
struct llist_head list_entry;
|
||||
|
||||
struct bsc_connection *bsc;
|
||||
|
||||
struct sccp_source_reference real_ref;
|
||||
struct sccp_source_reference patched_ref;
|
||||
struct sccp_source_reference remote_ref;
|
||||
int has_remote_ref;
|
||||
|
||||
/* GSM audio handling. That is 32 * multiplex + ts */
|
||||
int crcx;
|
||||
int msc_timeslot;
|
||||
int bsc_timeslot;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stats per BSC
|
||||
*/
|
||||
struct bsc_config_stats {
|
||||
struct {
|
||||
struct counter *conn;
|
||||
struct counter *calls;
|
||||
} sccp;
|
||||
|
||||
struct {
|
||||
struct counter *reconn;
|
||||
} net;
|
||||
};
|
||||
|
||||
/**
|
||||
* One BSC entry in the config
|
||||
*/
|
||||
struct bsc_config {
|
||||
struct llist_head entry;
|
||||
|
||||
char *token;
|
||||
unsigned int lac;
|
||||
int nr;
|
||||
|
||||
/* imsi white and blacklist */
|
||||
char *imsi_allow;
|
||||
regex_t imsi_allow_re;
|
||||
char *imsi_deny;
|
||||
regex_t imsi_deny_re;
|
||||
|
||||
int forbid_paging;
|
||||
|
||||
/* backpointer */
|
||||
struct bsc_nat *nat;
|
||||
|
||||
struct bsc_config_stats stats;
|
||||
};
|
||||
|
||||
/**
|
||||
* BSCs point of view of endpoints
|
||||
*/
|
||||
struct bsc_endpoint {
|
||||
/* the pending transaction id */
|
||||
char *transaction_id;
|
||||
/* the bsc we are talking to */
|
||||
struct bsc_connection *bsc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Statistic for the nat.
|
||||
*/
|
||||
struct bsc_nat_statistics {
|
||||
struct {
|
||||
struct counter *conn;
|
||||
struct counter *calls;
|
||||
} sccp;
|
||||
|
||||
struct {
|
||||
struct counter *reconn;
|
||||
struct counter *auth_fail;
|
||||
} bsc;
|
||||
|
||||
struct {
|
||||
struct counter *reconn;
|
||||
} msc;
|
||||
};
|
||||
|
||||
/**
|
||||
* the structure of the "nat" network
|
||||
*/
|
||||
struct bsc_nat {
|
||||
/* active SCCP connections that need patching */
|
||||
struct llist_head sccp_connections;
|
||||
|
||||
/* active BSC connections that need patching */
|
||||
struct llist_head bsc_connections;
|
||||
|
||||
/* known BSC's */
|
||||
struct llist_head bsc_configs;
|
||||
int num_bsc;
|
||||
|
||||
/* MGCP config */
|
||||
struct mgcp_config *mgcp_cfg;
|
||||
struct write_queue mgcp_queue;
|
||||
u_int8_t mgcp_msg[4096];
|
||||
int mgcp_length;
|
||||
|
||||
/* msc things */
|
||||
char *msc_ip;
|
||||
int msc_port;
|
||||
int first_contact;
|
||||
struct bsc_msc_connection *msc_con;
|
||||
|
||||
/* timeouts */
|
||||
int auth_timeout;
|
||||
int ping_timeout;
|
||||
int pong_timeout;
|
||||
|
||||
struct bsc_endpoint *bsc_endpoints;
|
||||
|
||||
/* filter */
|
||||
char *imsi_allow;
|
||||
regex_t imsi_allow_re;
|
||||
char *imsi_deny;
|
||||
regex_t imsi_deny_re;
|
||||
|
||||
/* statistics */
|
||||
struct bsc_nat_statistics stats;
|
||||
};
|
||||
|
||||
/* create and init the structures */
|
||||
struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token, unsigned int lac);
|
||||
struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num);
|
||||
struct bsc_nat *bsc_nat_alloc(void);
|
||||
struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat);
|
||||
void bsc_nat_set_msc_ip(struct bsc_nat *bsc, const char *ip);
|
||||
|
||||
void sccp_connection_destroy(struct sccp_connections *);
|
||||
void bsc_close_connection(struct bsc_connection *);
|
||||
|
||||
/**
|
||||
* parse the given message into the above structure
|
||||
*/
|
||||
struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg);
|
||||
|
||||
/**
|
||||
* filter based on IP Access header in both directions
|
||||
*/
|
||||
int bsc_nat_filter_ipa(int direction, struct msgb *msg, struct bsc_nat_parsed *parsed);
|
||||
int bsc_nat_vty_init(struct bsc_nat *nat);
|
||||
struct bsc_connection *bsc_nat_find_bsc(struct bsc_nat *nat, struct msgb *msg, int *_lac);
|
||||
|
||||
/**
|
||||
* SCCP patching and handling
|
||||
*/
|
||||
int create_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed);
|
||||
int update_sccp_src_ref(struct sccp_connections *sccp, struct bsc_nat_parsed *parsed);
|
||||
void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed);
|
||||
struct sccp_connections *patch_sccp_src_ref_to_bsc(struct msgb *, struct bsc_nat_parsed *, struct bsc_nat *);
|
||||
struct sccp_connections *patch_sccp_src_ref_to_msc(struct msgb *, struct bsc_nat_parsed *, struct bsc_connection *);
|
||||
|
||||
/**
|
||||
* MGCP/Audio handling
|
||||
*/
|
||||
int bsc_write_mgcp(struct bsc_connection *bsc, const u_int8_t *data, unsigned int length);
|
||||
int bsc_mgcp_assign(struct sccp_connections *, struct msgb *msg);
|
||||
void bsc_mgcp_init(struct sccp_connections *);
|
||||
void bsc_mgcp_dlcx(struct sccp_connections *);
|
||||
void bsc_mgcp_free_endpoints(struct bsc_nat *nat);
|
||||
int bsc_mgcp_nat_init(struct bsc_nat *nat);
|
||||
|
||||
struct sccp_connections *bsc_mgcp_find_con(struct bsc_nat *, int endpoint_number);
|
||||
struct msgb *bsc_mgcp_rewrite(char *input, int length, const char *ip, int port);
|
||||
void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg);
|
||||
|
||||
void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc);
|
||||
int bsc_mgcp_parse_response(const char *str, int *code, char transaction[60]);
|
||||
int bsc_mgcp_extract_ci(const char *resp);
|
||||
|
||||
|
||||
int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int id);
|
||||
|
||||
#endif
|
||||
333
openbsc/include/openbsc/bssap.h
Normal file
333
openbsc/include/openbsc/bssap.h
Normal file
@@ -0,0 +1,333 @@
|
||||
/* From GSM08.08 */
|
||||
|
||||
#ifndef BSSAP_H
|
||||
#define BSSAP_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <osmocore/msgb.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
|
||||
/*
|
||||
* this is from GSM 03.03 CGI but is copied in GSM 08.08
|
||||
* in § 3.2.2.27 for Cell Identifier List
|
||||
*/
|
||||
enum CELL_IDENT {
|
||||
CELL_IDENT_WHOLE_GLOBAL = 0,
|
||||
CELL_IDENT_LAC_AND_CI = 1,
|
||||
CELL_IDENT_CI = 2,
|
||||
CELL_IDENT_NO_CELL = 3,
|
||||
CELL_IDENT_LAI_AND_LAC = 4,
|
||||
CELL_IDENT_LAC = 5,
|
||||
CELL_IDENT_BSS = 6,
|
||||
CELL_IDENT_UTRAN_PLMN_LAC_RNC = 8,
|
||||
CELL_IDENT_UTRAN_RNC = 9,
|
||||
CELL_IDENT_UTRAN_LAC_RNC = 10,
|
||||
};
|
||||
|
||||
|
||||
/* GSM 08.06 § 6.3 */
|
||||
enum BSSAP_MSG_TYPE {
|
||||
BSSAP_MSG_BSS_MANAGEMENT = 0x0,
|
||||
BSSAP_MSG_DTAP = 0x1,
|
||||
};
|
||||
|
||||
struct bssmap_header {
|
||||
u_int8_t type;
|
||||
u_int8_t length;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dtap_header {
|
||||
u_int8_t type;
|
||||
u_int8_t link_id;
|
||||
u_int8_t length;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
enum BSS_MAP_MSG_TYPE {
|
||||
BSS_MAP_MSG_RESERVED_0 = 0,
|
||||
|
||||
/* ASSIGNMENT MESSAGES */
|
||||
BSS_MAP_MSG_ASSIGMENT_RQST = 1,
|
||||
BSS_MAP_MSG_ASSIGMENT_COMPLETE = 2,
|
||||
BSS_MAP_MSG_ASSIGMENT_FAILURE = 3,
|
||||
|
||||
/* HANDOVER MESSAGES */
|
||||
BSS_MAP_MSG_HANDOVER_RQST = 16,
|
||||
BSS_MAP_MSG_HANDOVER_REQUIRED = 17,
|
||||
BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE= 18,
|
||||
BSS_MAP_MSG_HANDOVER_CMD = 19,
|
||||
BSS_MAP_MSG_HANDOVER_COMPLETE = 20,
|
||||
BSS_MAP_MSG_HANDOVER_SUCCEEDED = 21,
|
||||
BSS_MAP_MSG_HANDOVER_FAILURE = 22,
|
||||
BSS_MAP_MSG_HANDOVER_PERFORMED = 23,
|
||||
BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE = 24,
|
||||
BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE = 25,
|
||||
BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT = 26,
|
||||
BSS_MAP_MSG_HANDOVER_DETECT = 27,
|
||||
|
||||
/* RELEASE MESSAGES */
|
||||
BSS_MAP_MSG_CLEAR_CMD = 32,
|
||||
BSS_MAP_MSG_CLEAR_COMPLETE = 33,
|
||||
BSS_MAP_MSG_CLEAR_RQST = 34,
|
||||
BSS_MAP_MSG_RESERVED_1 = 35,
|
||||
BSS_MAP_MSG_RESERVED_2 = 36,
|
||||
BSS_MAP_MSG_SAPI_N_REJECT = 37,
|
||||
BSS_MAP_MSG_CONFUSION = 38,
|
||||
|
||||
/* OTHER CONNECTION RELATED MESSAGES */
|
||||
BSS_MAP_MSG_SUSPEND = 40,
|
||||
BSS_MAP_MSG_RESUME = 41,
|
||||
BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION = 42,
|
||||
BSS_MAP_MSG_PERFORM_LOCATION_RQST = 43,
|
||||
BSS_MAP_MSG_LSA_INFORMATION = 44,
|
||||
BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE = 45,
|
||||
BSS_MAP_MSG_PERFORM_LOCATION_ABORT = 46,
|
||||
BSS_MAP_MSG_COMMON_ID = 47,
|
||||
|
||||
/* GENERAL MESSAGES */
|
||||
BSS_MAP_MSG_RESET = 48,
|
||||
BSS_MAP_MSG_RESET_ACKNOWLEDGE = 49,
|
||||
BSS_MAP_MSG_OVERLOAD = 50,
|
||||
BSS_MAP_MSG_RESERVED_3 = 51,
|
||||
BSS_MAP_MSG_RESET_CIRCUIT = 52,
|
||||
BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE = 53,
|
||||
BSS_MAP_MSG_MSC_INVOKE_TRACE = 54,
|
||||
BSS_MAP_MSG_BSS_INVOKE_TRACE = 55,
|
||||
BSS_MAP_MSG_CONNECTIONLESS_INFORMATION = 58,
|
||||
|
||||
/* TERRESTRIAL RESOURCE MESSAGES */
|
||||
BSS_MAP_MSG_BLOCK = 64,
|
||||
BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE = 65,
|
||||
BSS_MAP_MSG_UNBLOCK = 66,
|
||||
BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE = 67,
|
||||
BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK = 68,
|
||||
BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE = 69,
|
||||
BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK = 70,
|
||||
BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE = 71,
|
||||
BSS_MAP_MSG_UNEQUIPPED_CIRCUIT = 72,
|
||||
BSS_MAP_MSG_CHANGE_CIRCUIT = 78,
|
||||
BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE = 79,
|
||||
|
||||
/* RADIO RESOURCE MESSAGES */
|
||||
BSS_MAP_MSG_RESOURCE_RQST = 80,
|
||||
BSS_MAP_MSG_RESOURCE_INDICATION = 81,
|
||||
BSS_MAP_MSG_PAGING = 82,
|
||||
BSS_MAP_MSG_CIPHER_MODE_CMD = 83,
|
||||
BSS_MAP_MSG_CLASSMARK_UPDATE = 84,
|
||||
BSS_MAP_MSG_CIPHER_MODE_COMPLETE = 85,
|
||||
BSS_MAP_MSG_QUEUING_INDICATION = 86,
|
||||
BSS_MAP_MSG_COMPLETE_LAYER_3 = 87,
|
||||
BSS_MAP_MSG_CLASSMARK_RQST = 88,
|
||||
BSS_MAP_MSG_CIPHER_MODE_REJECT = 89,
|
||||
BSS_MAP_MSG_LOAD_INDICATION = 90,
|
||||
|
||||
/* VGCS/VBS */
|
||||
BSS_MAP_MSG_VGCS_VBS_SETUP = 4,
|
||||
BSS_MAP_MSG_VGCS_VBS_SETUP_ACK = 5,
|
||||
BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE = 6,
|
||||
BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST = 7,
|
||||
BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT = 28,
|
||||
BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE = 29,
|
||||
BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION = 30,
|
||||
BSS_MAP_MSG_UPLINK_RQST = 31,
|
||||
BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE = 39,
|
||||
BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION = 73,
|
||||
BSS_MAP_MSG_UPLINK_RELEASE_INDICATION = 74,
|
||||
BSS_MAP_MSG_UPLINK_REJECT_CMD = 75,
|
||||
BSS_MAP_MSG_UPLINK_RELEASE_CMD = 76,
|
||||
BSS_MAP_MSG_UPLINK_SEIZED_CMD = 77,
|
||||
};
|
||||
|
||||
enum GSM0808_IE_CODING {
|
||||
GSM0808_IE_CIRCUIT_IDENTITY_CODE = 1,
|
||||
GSM0808_IE_RESERVED_0 = 2,
|
||||
GSM0808_IE_RESOURCE_AVAILABLE = 3,
|
||||
GSM0808_IE_CAUSE = 4,
|
||||
GSM0808_IE_CELL_IDENTIFIER = 5,
|
||||
GSM0808_IE_PRIORITY = 6,
|
||||
GSM0808_IE_LAYER_3_HEADER_INFORMATION = 7,
|
||||
GSM0808_IE_IMSI = 8,
|
||||
GSM0808_IE_TMSI = 9,
|
||||
GSM0808_IE_ENCRYPTION_INFORMATION = 10,
|
||||
GSM0808_IE_CHANNEL_TYPE = 11,
|
||||
GSM0808_IE_PERIODICITY = 12,
|
||||
GSM0808_IE_EXTENDED_RESOURCE_INDICATOR = 13,
|
||||
GSM0808_IE_NUMBER_OF_MSS = 14,
|
||||
GSM0808_IE_RESERVED_1 = 15,
|
||||
GSM0808_IE_RESERVED_2 = 16,
|
||||
GSM0808_IE_RESERVED_3 = 17,
|
||||
GSM0808_IE_CLASSMARK_INFORMATION_T2 = 18,
|
||||
GSM0808_IE_CLASSMARK_INFORMATION_T3 = 19,
|
||||
GSM0808_IE_INTERFERENCE_BAND_TO_USE = 20,
|
||||
GSM0808_IE_RR_CAUSE = 21,
|
||||
GSM0808_IE_RESERVED_4 = 22,
|
||||
GSM0808_IE_LAYER_3_INFORMATION = 23,
|
||||
GSM0808_IE_DLCI = 24,
|
||||
GSM0808_IE_DOWNLINK_DTX_FLAG = 25,
|
||||
GSM0808_IE_CELL_IDENTIFIER_LIST = 26,
|
||||
GSM0808_IE_RESPONSE_RQST = 27,
|
||||
GSM0808_IE_RESOURCE_INDICATION_METHOD = 28,
|
||||
GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1 = 29,
|
||||
GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST = 30,
|
||||
GSM0808_IE_DIAGNOSTIC = 31,
|
||||
GSM0808_IE_LAYER_3_MESSAGE_CONTENTS = 32,
|
||||
GSM0808_IE_CHOSEN_CHANNEL = 33,
|
||||
GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE = 34,
|
||||
GSM0808_IE_CIPHER_RESPONSE_MODE = 35,
|
||||
GSM0808_IE_CHANNEL_NEEDED = 36,
|
||||
GSM0808_IE_TRACE_TYPE = 37,
|
||||
GSM0808_IE_TRIGGERID = 38,
|
||||
GSM0808_IE_TRACE_REFERENCE = 39,
|
||||
GSM0808_IE_TRANSACTIONID = 40,
|
||||
GSM0808_IE_MOBILE_IDENTITY = 41,
|
||||
GSM0808_IE_OMCID = 42,
|
||||
GSM0808_IE_FORWARD_INDICATOR = 43,
|
||||
GSM0808_IE_CHOSEN_ENCR_ALG = 44,
|
||||
GSM0808_IE_CIRCUIT_POOL = 45,
|
||||
GSM0808_IE_CIRCUIT_POOL_LIST = 46,
|
||||
GSM0808_IE_TIME_INDICATION = 47,
|
||||
GSM0808_IE_RESOURCE_SITUATION = 48,
|
||||
GSM0808_IE_CURRENT_CHANNEL_TYPE_1 = 49,
|
||||
GSM0808_IE_QUEUEING_INDICATOR = 50,
|
||||
GSM0808_IE_SPEECH_VERSION = 64,
|
||||
GSM0808_IE_ASSIGNMENT_REQUIREMENT = 51,
|
||||
GSM0808_IE_TALKER_FLAG = 53,
|
||||
GSM0808_IE_CONNECTION_RELEASE_RQSTED = 54,
|
||||
GSM0808_IE_GROUP_CALL_REFERENCE = 55,
|
||||
GSM0808_IE_EMLPP_PRIORITY = 56,
|
||||
GSM0808_IE_CONFIG_EVO_INDI = 57,
|
||||
GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION = 58,
|
||||
GSM0808_IE_LSA_IDENTIFIER = 59,
|
||||
GSM0808_IE_LSA_IDENTIFIER_LIST = 60,
|
||||
GSM0808_IE_LSA_INFORMATION = 61,
|
||||
GSM0808_IE_LCS_QOS = 62,
|
||||
GSM0808_IE_LSA_ACCESS_CTRL_SUPPR = 63,
|
||||
GSM0808_IE_LCS_PRIORITY = 67,
|
||||
GSM0808_IE_LOCATION_TYPE = 68,
|
||||
GSM0808_IE_LOCATION_ESTIMATE = 69,
|
||||
GSM0808_IE_POSITIONING_DATA = 70,
|
||||
GSM0808_IE_LCS_CAUSE = 71,
|
||||
GSM0808_IE_LCS_CLIENT_TYPE = 72,
|
||||
GSM0808_IE_APDU = 73,
|
||||
GSM0808_IE_NETWORK_ELEMENT_IDENTITY = 74,
|
||||
GSM0808_IE_GPS_ASSISTANCE_DATA = 75,
|
||||
GSM0808_IE_DECIPHERING_KEYS = 76,
|
||||
GSM0808_IE_RETURN_ERROR_RQST = 77,
|
||||
GSM0808_IE_RETURN_ERROR_CAUSE = 78,
|
||||
GSM0808_IE_SEGMENTATION = 79,
|
||||
GSM0808_IE_SERVICE_HANDOVER = 80,
|
||||
GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS = 81,
|
||||
GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82,
|
||||
GSM0808_IE_RESERVED_5 = 65,
|
||||
GSM0808_IE_RESERVED_6 = 66,
|
||||
};
|
||||
|
||||
enum gsm0808_cause {
|
||||
GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE = 0,
|
||||
GSM0808_CAUSE_RADIO_INTERFACE_FAILURE = 1,
|
||||
GSM0808_CAUSE_UPLINK_QUALITY = 2,
|
||||
GSM0808_CAUSE_UPLINK_STRENGTH = 3,
|
||||
GSM0808_CAUSE_DOWNLINK_QUALITY = 4,
|
||||
GSM0808_CAUSE_DOWNLINK_STRENGTH = 5,
|
||||
GSM0808_CAUSE_DISTANCE = 6,
|
||||
GSM0808_CAUSE_O_AND_M_INTERVENTION = 7,
|
||||
GSM0808_CAUSE_RESPONSE_TO_MSC_INVOCATION = 8,
|
||||
GSM0808_CAUSE_CALL_CONTROL = 9,
|
||||
GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION = 10,
|
||||
GSM0808_CAUSE_HANDOVER_SUCCESSFUL = 11,
|
||||
GSM0808_CAUSE_BETTER_CELL = 12,
|
||||
GSM0808_CAUSE_DIRECTED_RETRY = 13,
|
||||
GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL = 14,
|
||||
GSM0808_CAUSE_TRAFFIC = 15,
|
||||
GSM0808_CAUSE_EQUIPMENT_FAILURE = 32,
|
||||
GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE = 33,
|
||||
GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE = 34,
|
||||
GSM0808_CAUSE_CCCH_OVERLOAD = 35,
|
||||
GSM0808_CAUSE_PROCESSOR_OVERLOAD = 36,
|
||||
GSM0808_CAUSE_BSS_NOT_EQUIPPED = 37,
|
||||
GSM0808_CAUSE_MS_NOT_EQUIPPED = 38,
|
||||
GSM0808_CAUSE_INVALID_CELL = 39,
|
||||
GSM0808_CAUSE_TRAFFIC_LOAD = 40,
|
||||
GSM0808_CAUSE_PREEMPTION = 41,
|
||||
GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE = 48,
|
||||
GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH = 49,
|
||||
GSM0808_CAUSE_SWITCH_CIRCUIT_POOL = 50,
|
||||
GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE = 51,
|
||||
GSM0808_CAUSE_LSA_NOT_ALLOWED = 52,
|
||||
GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED = 64,
|
||||
GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED = 80,
|
||||
GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS = 81,
|
||||
GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING = 82,
|
||||
GSM0808_CAUSE_INCORRECT_VALUE = 83,
|
||||
GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE = 84,
|
||||
GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT = 85,
|
||||
GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC = 96,
|
||||
};
|
||||
|
||||
/* GSM 08.08 3.2.2.11 Channel Type */
|
||||
enum gsm0808_chan_indicator {
|
||||
GSM0808_CHAN_SPEECH = 1,
|
||||
GSM0808_CHAN_DATA = 2,
|
||||
GSM0808_CHAN_SIGN = 3,
|
||||
};
|
||||
|
||||
enum gsm0808_chan_rate_type_data {
|
||||
GSM0808_DATA_FULL_BM = 0x8,
|
||||
GSM0808_DATA_HALF_LM = 0x9,
|
||||
GSM0808_DATA_FULL_RPREF = 0xa,
|
||||
GSM0808_DATA_HALF_PREF = 0xb,
|
||||
GSM0808_DATA_FULL_PREF_NO_CHANGE = 0x1a,
|
||||
GSM0808_DATA_HALF_PREF_NO_CHANGE = 0x1b,
|
||||
GSM0808_DATA_MULTI_MASK = 0x20,
|
||||
GSM0808_DATA_MULTI_MASK_NO_CHANGE = 0x30,
|
||||
};
|
||||
|
||||
enum gsm0808_chan_rate_type_speech {
|
||||
GSM0808_SPEECH_FULL_BM = 0x8,
|
||||
GSM0808_SPEECH_HALF_LM = 0x9,
|
||||
GSM0808_SPEECH_FULL_PREF= 0xa,
|
||||
GSM0808_SPEECH_HALF_PREF= 0xb,
|
||||
GSM0808_SPEECH_FULL_PREF_NO_CHANGE = 0x1a,
|
||||
GSM0808_SPEECH_HALF_PREF_NO_CHANGE = 0x1b,
|
||||
GSM0808_SPEECH_PERM = 0xf,
|
||||
GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f,
|
||||
};
|
||||
|
||||
enum gsm0808_permitted_speech {
|
||||
GSM0808_PERM_FR1 = 0x01,
|
||||
GSM0808_PERM_FR2 = 0x11,
|
||||
GSM0808_PERM_FR3 = 0x21,
|
||||
GSM0808_PERM_HR1 = GSM0808_PERM_FR1 | 0x4,
|
||||
GSM0808_PERM_HR2 = GSM0808_PERM_FR2 | 0x4,
|
||||
GSM0808_PERM_HR3 = GSM0808_PERM_FR3 | 0x4,
|
||||
};
|
||||
|
||||
int bssmap_rcvmsg_dt1(struct sccp_connection *conn, struct msgb *msg, unsigned int length);
|
||||
int bssmap_rcvmsg_udt(struct gsm_network *net, struct msgb *msg, unsigned int length);
|
||||
|
||||
struct msgb *bssmap_create_layer3(struct msgb *msg);
|
||||
struct msgb *bssmap_create_reset(void);
|
||||
struct msgb *bssmap_create_clear_complete(void);
|
||||
struct msgb *bssmap_create_cipher_complete(struct msgb *layer3);
|
||||
struct msgb *bssmap_create_cipher_reject(u_int8_t cause);
|
||||
struct msgb *bssmap_create_sapi_reject(u_int8_t link_id);
|
||||
struct msgb *bssmap_create_assignment_completed(struct gsm_lchan *lchan, u_int8_t rr_cause);
|
||||
struct msgb *bssmap_create_assignment_failure(u_int8_t cause, u_int8_t *rr_cause);
|
||||
struct msgb *bssmap_create_classmark_update(const u_int8_t *classmark, u_int8_t length);
|
||||
|
||||
void gsm0808_send_assignment_failure(struct gsm_lchan *l, u_int8_t cause, u_int8_t *rr_value);
|
||||
void gsm0808_send_assignment_compl(struct gsm_lchan *l, u_int8_t rr_value);
|
||||
|
||||
int dtap_rcvmsg(struct gsm_lchan *lchan, struct msgb *msg, unsigned int length);
|
||||
struct msgb *dtap_create_msg(struct msgb *msg_l3, u_int8_t link_id);
|
||||
|
||||
void bsc_queue_connection_write(struct sccp_connection *conn, struct msgb *msg);
|
||||
void bsc_free_queued(struct sccp_connection *conn);
|
||||
void bsc_send_queued(struct sccp_connection *conn);
|
||||
|
||||
void bts_send_queued(struct bss_sccp_connection_data*);
|
||||
void bts_free_queued(struct bss_sccp_connection_data*);
|
||||
void bts_unblock_queue(struct bss_sccp_connection_data*);
|
||||
|
||||
#endif
|
||||
@@ -23,6 +23,28 @@
|
||||
|
||||
#include "gsm_subscriber.h"
|
||||
|
||||
/*
|
||||
* Refcounting for the lchan. If the refcount drops to zero
|
||||
* the channel will send a RSL release request.
|
||||
*/
|
||||
#define use_subscr_con(con) \
|
||||
do { (con)->use_count++; \
|
||||
DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \
|
||||
(con)->lchan->ts->trx->bts->nr, (con)->lchan->ts->trx->nr, (con)->lchan->ts->nr, \
|
||||
(con)->lchan->nr, (con)->use_count); \
|
||||
} while(0);
|
||||
|
||||
#define put_subscr_con(con, reason) \
|
||||
do { (con)->use_count--; \
|
||||
DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) decreases usage to: %d\n", \
|
||||
(con)->lchan->ts->trx->bts->nr, (con)->lchan->ts->trx->nr, (con)->lchan->ts->nr, \
|
||||
(con)->lchan->nr, (con)->use_count); \
|
||||
if ((con)->use_count <= 0) \
|
||||
_lchan_release((con)->lchan, reason); \
|
||||
} while(0);
|
||||
|
||||
|
||||
|
||||
/* Special allocator for C0 of BTS */
|
||||
struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts,
|
||||
enum gsm_phys_chan_config pchan);
|
||||
@@ -41,13 +63,14 @@ struct gsm_lchan *lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr)
|
||||
struct gsm_lchan *lchan_for_subscr(struct gsm_subscriber *subscr);
|
||||
|
||||
/* Allocate a logical channel (SDCCH, TCH, ...) */
|
||||
struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type);
|
||||
struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, int allow_bigger);
|
||||
|
||||
/* Free a logical channel (SDCCH, TCH, ...) */
|
||||
void lchan_free(struct gsm_lchan *lchan);
|
||||
void lchan_reset(struct gsm_lchan *lchan);
|
||||
|
||||
/* Consider releasing the channel */
|
||||
int lchan_auto_release(struct gsm_lchan *lchan);
|
||||
/* internal.. do not use */
|
||||
int _lchan_release(struct gsm_lchan *lchan, u_int8_t release_reason);
|
||||
|
||||
struct load_counter {
|
||||
unsigned int total;
|
||||
|
||||
@@ -55,8 +55,8 @@ int set_authtuple_for_subscr(struct gsm_auth_tuple *atuple,
|
||||
|
||||
/* SMS store-and-forward */
|
||||
int db_sms_store(struct gsm_sms *sms);
|
||||
struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id);
|
||||
struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, int min_subscr_id);
|
||||
struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id);
|
||||
struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, unsigned long long min_subscr_id);
|
||||
struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr);
|
||||
int db_sms_mark_sent(struct gsm_sms *sms);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <osmocore/linuxlist.h>
|
||||
|
||||
#define DEBUG
|
||||
#include <osmocore/logging.h>
|
||||
|
||||
/* Debug Areas of the code */
|
||||
enum {
|
||||
@@ -28,34 +29,10 @@ enum {
|
||||
DHO,
|
||||
DDB,
|
||||
DREF,
|
||||
DNAT,
|
||||
Debug_LastEntry,
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
|
||||
#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
|
||||
#else
|
||||
#define DEBUGP(xss, fmt, args...)
|
||||
#define DEBUGPC(ss, fmt, args...)
|
||||
#endif
|
||||
|
||||
|
||||
#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
|
||||
|
||||
char *hexdump(const unsigned char *buf, int len);
|
||||
void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
|
||||
|
||||
/* new logging interface */
|
||||
#define LOGP(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
|
||||
#define LOGPC(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)
|
||||
|
||||
/* different levels */
|
||||
#define LOGL_DEBUG 1 /* debugging information */
|
||||
#define LOGL_INFO 3
|
||||
#define LOGL_NOTICE 5 /* abnormal/unexpected condition */
|
||||
#define LOGL_ERROR 7 /* error condition, requires user action */
|
||||
#define LOGL_FATAL 8 /* fatal, program aborted */
|
||||
|
||||
/* context */
|
||||
#define BSC_CTX_LCHAN 0
|
||||
#define BSC_CTX_SUBSCR 1
|
||||
@@ -65,67 +42,12 @@ void debugp(unsigned int subsys, char *file, int line, int cont, const char *for
|
||||
/* target */
|
||||
|
||||
enum {
|
||||
DEBUG_FILTER_IMSI = 1 << 0,
|
||||
DEBUG_FILTER_ALL = 1 << 1,
|
||||
//DEBUG_FILTER_ALL = 1 << 0,
|
||||
LOG_FILTER_IMSI = 1 << 1,
|
||||
};
|
||||
|
||||
struct debug_category {
|
||||
int enabled;
|
||||
int loglevel;
|
||||
};
|
||||
void log_set_imsi_filter(struct log_target *target, const char *imsi);
|
||||
|
||||
struct debug_target {
|
||||
int filter_map;
|
||||
char *imsi_filter;
|
||||
extern const struct log_info log_info;
|
||||
|
||||
|
||||
struct debug_category categories[Debug_LastEntry];
|
||||
int use_color;
|
||||
int print_timestamp;
|
||||
int loglevel;
|
||||
|
||||
union {
|
||||
struct {
|
||||
FILE *out;
|
||||
} tgt_stdout;
|
||||
|
||||
struct {
|
||||
int priority;
|
||||
} tgt_syslog;
|
||||
|
||||
struct {
|
||||
void *vty;
|
||||
} tgt_vty;
|
||||
};
|
||||
|
||||
void (*output) (struct debug_target *target, const char *string);
|
||||
|
||||
struct llist_head entry;
|
||||
};
|
||||
|
||||
/* use the above macros */
|
||||
void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 6, 7)));
|
||||
void debug_init(void);
|
||||
|
||||
/* context management */
|
||||
void debug_reset_context(void);
|
||||
void debug_set_context(int ctx, void *value);
|
||||
|
||||
/* filter on the targets */
|
||||
void debug_set_imsi_filter(struct debug_target *target, const char *imsi);
|
||||
void debug_set_all_filter(struct debug_target *target, int);
|
||||
void debug_set_use_color(struct debug_target *target, int);
|
||||
void debug_set_print_timestamp(struct debug_target *target, int);
|
||||
void debug_set_log_level(struct debug_target *target, int log_level);
|
||||
void debug_parse_category_mask(struct debug_target *target, const char* mask);
|
||||
int debug_parse_level(const char *lvl);
|
||||
int debug_parse_category(const char *category);
|
||||
void debug_set_category_filter(struct debug_target *target, int category, int enable, int level);
|
||||
|
||||
|
||||
/* management of the targets */
|
||||
struct debug_target *debug_target_create(void);
|
||||
struct debug_target *debug_target_create_stderr(void);
|
||||
void debug_add_target(struct debug_target *target);
|
||||
void debug_del_target(struct debug_target *target);
|
||||
#endif /* _DEBUG_H */
|
||||
|
||||
@@ -66,6 +66,8 @@ struct e1inp_ts {
|
||||
struct {
|
||||
/* list of all signalling links on this TS */
|
||||
struct llist_head sign_links;
|
||||
/* delay for the queue */
|
||||
int delay;
|
||||
/* timer when to dequeue next frame */
|
||||
struct timer_list tx_timer;
|
||||
} sign;
|
||||
@@ -93,6 +95,7 @@ struct e1inp_driver {
|
||||
struct llist_head list;
|
||||
const char *name;
|
||||
int (*want_write)(struct e1inp_ts *ts);
|
||||
int default_delay;
|
||||
};
|
||||
|
||||
struct e1inp_line {
|
||||
|
||||
@@ -16,15 +16,15 @@ struct gsm_trans;
|
||||
void gsm0408_allow_everyone(int allow);
|
||||
|
||||
int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id);
|
||||
enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci);
|
||||
enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci);
|
||||
enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *bts, u_int8_t ra);
|
||||
enum gsm_chreq_reason_t get_reason_by_chreq(u_int8_t ra, int neci);
|
||||
void gsm_net_update_ctype(struct gsm_network *net);
|
||||
|
||||
int gsm48_tx_mm_info(struct gsm_lchan *lchan);
|
||||
int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand, int key_seq);
|
||||
int gsm48_tx_mm_auth_rej(struct gsm_lchan *lchan);
|
||||
struct msgb *gsm48_msgb_alloc(void);
|
||||
int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans);
|
||||
int gsm48_mi_to_string(char *string, const int str_len, const u_int8_t *mi, const int mi_len);
|
||||
|
||||
int gsm48_send_rr_release(struct gsm_lchan *lchan);
|
||||
int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv);
|
||||
|
||||
@@ -25,7 +25,7 @@ struct msgb;
|
||||
|
||||
int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id);
|
||||
|
||||
int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms);
|
||||
int gsm411_send_sms_lchan(struct gsm_subscriber_connection *conn, struct gsm_sms *sms);
|
||||
|
||||
struct gsm_sms *sms_alloc(void);
|
||||
void sms_free(struct gsm_sms *sms);
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
struct bsc_msc_connection;
|
||||
|
||||
enum gsm_phys_chan_config {
|
||||
GSM_PCHAN_NONE,
|
||||
GSM_PCHAN_CCCH,
|
||||
@@ -73,36 +75,19 @@ enum gsm_paging_event {
|
||||
GSM_PAGING_OOM,
|
||||
};
|
||||
|
||||
enum bts_gprs_mode {
|
||||
BTS_GPRS_NONE = 0,
|
||||
BTS_GPRS_GPRS = 1,
|
||||
BTS_GPRS_EGPRS = 2,
|
||||
};
|
||||
|
||||
struct msgb;
|
||||
typedef int gsm_cbfn(unsigned int hooknum,
|
||||
unsigned int event,
|
||||
struct msgb *msg,
|
||||
void *data, void *param);
|
||||
|
||||
/*
|
||||
* Use the channel. As side effect the lchannel recycle timer
|
||||
* will be started.
|
||||
*/
|
||||
#define LCHAN_RELEASE_TIMEOUT 20, 0
|
||||
#define use_lchan(lchan) \
|
||||
do { lchan->use_count++; \
|
||||
DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \
|
||||
lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \
|
||||
lchan->nr, lchan->use_count); \
|
||||
bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT); } while(0);
|
||||
|
||||
#define put_lchan(lchan) \
|
||||
do { lchan->use_count--; \
|
||||
DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) decreases usage to: %d\n", \
|
||||
lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, \
|
||||
lchan->nr, lchan->use_count); \
|
||||
} while(0);
|
||||
|
||||
|
||||
/* communications link with a BTS */
|
||||
struct gsm_bts_link {
|
||||
struct gsm_bts *bts;
|
||||
};
|
||||
struct sccp_connection;
|
||||
|
||||
/* Real authentication information containing Ki */
|
||||
enum gsm_auth_algo {
|
||||
@@ -131,6 +116,44 @@ struct gsm_subscriber;
|
||||
struct gsm_mncc;
|
||||
struct rtp_socket;
|
||||
|
||||
/* BSC/MSC data holding them together */
|
||||
struct bss_sccp_connection_data {
|
||||
struct gsm_lchan *lchan;
|
||||
struct gsm_lchan *secondary_lchan;
|
||||
struct sccp_connection *sccp;
|
||||
int ciphering_handled : 1;
|
||||
|
||||
/* Timers... */
|
||||
|
||||
/* for assginment command */
|
||||
struct timer_list T10;
|
||||
|
||||
/* for SCCP ... */
|
||||
struct timer_list sccp_cc_timeout;
|
||||
struct timer_list sccp_it;
|
||||
|
||||
/* audio handling */
|
||||
int rtp_port;
|
||||
|
||||
/* Queue SCCP and GSM0408 messages */
|
||||
int block_gsm;
|
||||
struct llist_head gsm_queue;
|
||||
unsigned int gsm_queue_size;
|
||||
|
||||
struct llist_head sccp_queue;
|
||||
unsigned int sccp_queue_size;
|
||||
|
||||
/* Active connections */
|
||||
struct llist_head active_connections;
|
||||
};
|
||||
|
||||
#define GSM0808_T10_VALUE 6, 0
|
||||
#define sccp_get_lchan(data_ctx) ((struct bss_sccp_connection_data *)data_ctx)->lchan
|
||||
#define lchan_get_sccp(lchan) lchan->msc_data->sccp
|
||||
struct bss_sccp_connection_data *bss_sccp_create_data();
|
||||
void bss_sccp_free_data(struct bss_sccp_connection_data *);
|
||||
|
||||
|
||||
/* Network Management State */
|
||||
struct gsm_nm_state {
|
||||
u_int8_t operational;
|
||||
@@ -179,9 +202,31 @@ enum gsm_lchan_state {
|
||||
LCHAN_S_ACT_REQ, /* channel activatin requested */
|
||||
LCHAN_S_ACTIVE, /* channel is active and operational */
|
||||
LCHAN_S_REL_REQ, /* channel release has been requested */
|
||||
LCHAN_S_REL_ERR, /* channel is in an error state */
|
||||
LCHAN_S_INACTIVE, /* channel is set inactive */
|
||||
};
|
||||
|
||||
/* the per subscriber data for lchan */
|
||||
struct gsm_subscriber_connection {
|
||||
/* To whom we are allocated at the moment */
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
/*
|
||||
* Operations that have a state and might be pending
|
||||
*/
|
||||
struct gsm_loc_updating_operation *loc_operation;
|
||||
|
||||
/* use count. how many users use this channel */
|
||||
unsigned int use_count;
|
||||
|
||||
/* Are we part of a special "silent" call */
|
||||
int silent_call;
|
||||
|
||||
/* back pointers */
|
||||
struct gsm_lchan *lchan;
|
||||
struct gsm_bts *bts;
|
||||
};
|
||||
|
||||
struct gsm_lchan {
|
||||
/* The TS that we're part of */
|
||||
struct gsm_bts_trx_ts *ts;
|
||||
@@ -204,30 +249,22 @@ struct gsm_lchan {
|
||||
u_int8_t key_len;
|
||||
u_int8_t key[MAX_A5_KEY_LEN];
|
||||
} encr;
|
||||
/* Are we part of a special "silent" call */
|
||||
int silent_call;
|
||||
|
||||
struct timer_list T3101;
|
||||
struct timer_list T3111;
|
||||
struct timer_list error_timer;
|
||||
|
||||
/* AMR bits */
|
||||
struct gsm48_multi_rate_conf mr_conf;
|
||||
|
||||
/* To whom we are allocated at the moment */
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
/* Timer started to release the channel */
|
||||
struct timer_list release_timer;
|
||||
|
||||
struct timer_list T3101;
|
||||
|
||||
/* Established data link layer services */
|
||||
u_int8_t sapis[8];
|
||||
|
||||
/*
|
||||
* Operations that have a state and might be pending
|
||||
* MSC handling...
|
||||
*/
|
||||
struct gsm_loc_updating_operation *loc_operation;
|
||||
struct bss_sccp_connection_data *msc_data;
|
||||
|
||||
/* use count. how many users use this channel */
|
||||
unsigned int use_count;
|
||||
|
||||
/* cache of last measurement reports on this lchan */
|
||||
struct gsm_meas_rep meas_rep[6];
|
||||
@@ -246,6 +283,11 @@ struct gsm_lchan {
|
||||
u_int8_t speech_mode;
|
||||
struct rtp_socket *rtp_socket;
|
||||
} abis_ip;
|
||||
|
||||
struct gsm_subscriber_connection conn;
|
||||
|
||||
/* release reason */
|
||||
u_int8_t release_reason;
|
||||
};
|
||||
|
||||
struct gsm_e1_subslot {
|
||||
@@ -257,7 +299,7 @@ struct gsm_e1_subslot {
|
||||
u_int8_t e1_ts_ss;
|
||||
};
|
||||
|
||||
#define BTS_TRX_F_ACTIVATED 0x0001
|
||||
#define TS_F_PDCH_MODE 0x1000
|
||||
/* One Timeslot in a TRX */
|
||||
struct gsm_bts_trx_ts {
|
||||
struct gsm_bts_trx *trx;
|
||||
@@ -358,10 +400,13 @@ struct gsm_paging_request {
|
||||
struct gsm_bts_paging_state {
|
||||
/* pending requests */
|
||||
struct llist_head pending_requests;
|
||||
struct gsm_paging_request *last_request;
|
||||
struct gsm_bts *bts;
|
||||
|
||||
struct timer_list work_timer;
|
||||
struct timer_list credit_timer;
|
||||
|
||||
/* free chans needed */
|
||||
int free_chans_need;
|
||||
|
||||
/* load */
|
||||
u_int16_t available_slots;
|
||||
@@ -466,7 +511,7 @@ struct gsm_bts {
|
||||
|
||||
/* Not entirely sure how ip.access specific this is */
|
||||
struct {
|
||||
int enabled;
|
||||
enum bts_gprs_mode mode;
|
||||
struct {
|
||||
struct gsm_nm_state nm_state;
|
||||
u_int16_t nsei;
|
||||
@@ -478,6 +523,10 @@ struct gsm_bts {
|
||||
struct gsm_bts_gprs_nsvc nsvc[2];
|
||||
u_int8_t rac;
|
||||
} gprs;
|
||||
|
||||
/* RACH NM values */
|
||||
int rach_b_thresh;
|
||||
int rach_ldavg_slots;
|
||||
|
||||
/* transceivers */
|
||||
int num_trx;
|
||||
@@ -525,6 +574,14 @@ struct gsmnet_stats {
|
||||
struct counter *alerted; /* we alerted the other end */
|
||||
struct counter *connected;/* how many calls were accepted */
|
||||
} call;
|
||||
struct {
|
||||
struct counter *rf_fail;
|
||||
struct counter *rll_err;
|
||||
} chan;
|
||||
struct {
|
||||
struct counter *oml_fail;
|
||||
struct counter *rsl_fail;
|
||||
} bts;
|
||||
};
|
||||
|
||||
enum gsm_auth_policy {
|
||||
@@ -536,6 +593,14 @@ enum gsm_auth_policy {
|
||||
#define GSM_T3101_DEFAULT 10
|
||||
#define GSM_T3113_DEFAULT 60
|
||||
|
||||
/*
|
||||
* internal data for audio management
|
||||
*/
|
||||
struct gsm_audio_support {
|
||||
u_int8_t hr : 1,
|
||||
ver : 7;
|
||||
};
|
||||
|
||||
struct gsm_network {
|
||||
/* global parameters */
|
||||
u_int16_t country_code;
|
||||
@@ -566,6 +631,11 @@ struct gsm_network {
|
||||
|
||||
struct gsmnet_stats stats;
|
||||
|
||||
struct gsm_audio_support **audio_support;
|
||||
int audio_length;
|
||||
int rtp_payload;
|
||||
int rtp_base_port;
|
||||
|
||||
/* layer 4 */
|
||||
int (*mncc_recv) (struct gsm_network *net, int msg_type, void *arg);
|
||||
struct llist_head upqueue;
|
||||
@@ -591,6 +661,23 @@ struct gsm_network {
|
||||
struct {
|
||||
enum rrlp_mode mode;
|
||||
} rrlp;
|
||||
|
||||
enum gsm_chan_t ctype_by_chreq[16];
|
||||
|
||||
/* Use a TCH for handling requests of type paging any */
|
||||
int pag_any_tch;
|
||||
|
||||
/* a hack for On Waves. It must be signed */
|
||||
int32_t core_country_code;
|
||||
int32_t core_network_code;
|
||||
|
||||
/* a simple token for this network... */
|
||||
char *bsc_token;
|
||||
char *msc_ip;
|
||||
int msc_port;
|
||||
struct bsc_msc_connection *msc_con;
|
||||
int ping_timeout;
|
||||
int pong_timeout;
|
||||
};
|
||||
|
||||
#define SMS_HDR_SIZE 128
|
||||
@@ -687,15 +774,10 @@ const char *gsm_auth_policy_name(enum gsm_auth_policy policy);
|
||||
enum rrlp_mode rrlp_mode_parse(const char *arg);
|
||||
const char *rrlp_mode_name(enum rrlp_mode mode);
|
||||
|
||||
void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked);
|
||||
enum bts_gprs_mode bts_gprs_mode_parse(const char *arg);
|
||||
const char *bts_gprs_mode_name(enum bts_gprs_mode mode);
|
||||
|
||||
/* A parsed GPRS routing area */
|
||||
struct gprs_ra_id {
|
||||
u_int16_t mnc;
|
||||
u_int16_t mcc;
|
||||
u_int16_t lac;
|
||||
u_int8_t rac;
|
||||
};
|
||||
void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked);
|
||||
|
||||
int gsm48_ra_id_by_bts(u_int8_t *buf, struct gsm_bts *bts);
|
||||
void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts);
|
||||
|
||||
@@ -81,6 +81,8 @@ struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net,
|
||||
const char *ext);
|
||||
struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net,
|
||||
unsigned long long id);
|
||||
struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net,
|
||||
const char *imsi);
|
||||
int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason);
|
||||
void subscr_put_channel(struct gsm_lchan *lchan);
|
||||
void subscr_get_channel(struct gsm_subscriber *subscr,
|
||||
|
||||
@@ -53,6 +53,8 @@ int ipaccess_send_id_req(int fd);
|
||||
|
||||
int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len);
|
||||
|
||||
int ipaccess_drop_oml(struct gsm_bts *bts);
|
||||
int ipaccess_drop_rsl(struct gsm_bts_trx *trx);
|
||||
|
||||
/*
|
||||
* Firmware specific header
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define RTP_PORT_DEFAULT 4000
|
||||
|
||||
/**
|
||||
* Calculate the RTP audio port for the given multiplex
|
||||
* and the direction. This allows a semi static endpoint
|
||||
@@ -77,14 +76,17 @@ struct mgcp_config;
|
||||
|
||||
typedef int (*mgcp_change)(struct mgcp_config *cfg, int endpoint, int state, int local_rtp);
|
||||
typedef int (*mgcp_policy)(struct mgcp_config *cfg, int endpoint, int state, const char *transactio_id);
|
||||
typedef int (*mgcp_reset)(struct mgcp_config *cfg);
|
||||
|
||||
struct mgcp_config {
|
||||
/* common configuration */
|
||||
int source_port;
|
||||
char *local_ip;
|
||||
char *source_addr;
|
||||
unsigned int number_endpoints;
|
||||
char *bts_ip;
|
||||
char *call_agent_addr;
|
||||
|
||||
/* default endpoint data */
|
||||
struct in_addr bts_in;
|
||||
char *audio_name;
|
||||
int audio_payload;
|
||||
@@ -92,15 +94,24 @@ struct mgcp_config {
|
||||
int early_bind;
|
||||
int rtp_base_port;
|
||||
|
||||
/* only used in forward mode */
|
||||
char *forward_ip;
|
||||
int forward_port;
|
||||
|
||||
unsigned int last_call_id;
|
||||
|
||||
/* endpoint configuration */
|
||||
unsigned int number_endpoints;
|
||||
struct mgcp_endpoint *endpoints;
|
||||
|
||||
/* spec handling */
|
||||
int force_realloc;
|
||||
|
||||
/* callback functionality */
|
||||
mgcp_change change_cb;
|
||||
mgcp_policy policy_cb;
|
||||
mgcp_reset reset_cb;
|
||||
void *data;
|
||||
|
||||
struct mgcp_endpoint *endpoints;
|
||||
unsigned int last_call_id;
|
||||
};
|
||||
|
||||
/* config management */
|
||||
@@ -115,8 +126,15 @@ void mgcp_free_endp(struct mgcp_endpoint *endp);
|
||||
* format helper functions
|
||||
*/
|
||||
struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg);
|
||||
struct msgb *mgcp_create_rsip(void);
|
||||
struct msgb *mgcp_create_response_with_data(int code, const char *msg, const char *trans, const char *data);
|
||||
|
||||
/* adc helper */
|
||||
static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot)
|
||||
{
|
||||
if (timeslot == 0)
|
||||
timeslot = 1;
|
||||
return timeslot + (31 * multiplex);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -57,8 +57,22 @@ struct mgcp_endpoint {
|
||||
|
||||
/* backpointer */
|
||||
struct mgcp_config *cfg;
|
||||
|
||||
/* statistics */
|
||||
unsigned int in_bts;
|
||||
unsigned int in_remote;
|
||||
};
|
||||
|
||||
#define ENDPOINT_NUMBER(endp) abs(endp - endp->cfg->endpoints)
|
||||
|
||||
struct mgcp_msg_ptr {
|
||||
unsigned int start;
|
||||
unsigned int length;
|
||||
};
|
||||
|
||||
int mgcp_analyze_header(struct mgcp_config *cfg, struct msgb *msg,
|
||||
struct mgcp_msg_ptr *ptr, int size,
|
||||
const char **transaction_id, struct mgcp_endpoint **endp);
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -43,4 +43,7 @@ void paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr,
|
||||
/* update paging load */
|
||||
void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t);
|
||||
|
||||
/* pending paging requests */
|
||||
unsigned int paging_pending_requests_nr(struct gsm_bts *bts);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -71,6 +71,7 @@ enum gprs_nmo {
|
||||
GPRS_NMO_III = 2, /* no paging coordination */
|
||||
};
|
||||
|
||||
/* TS 04.60 12.24 */
|
||||
struct gprs_cell_options {
|
||||
enum gprs_nmo nmo;
|
||||
/* T3168: wait for packet uplink assignment message */
|
||||
@@ -79,6 +80,16 @@ struct gprs_cell_options {
|
||||
u_int32_t t3192; /* in milliseconds */
|
||||
u_int32_t drx_timer_max;/* in seconds */
|
||||
u_int32_t bs_cv_max;
|
||||
|
||||
u_int8_t ext_info_present;
|
||||
struct {
|
||||
u_int8_t egprs_supported;
|
||||
u_int8_t use_egprs_p_ch_req;
|
||||
u_int8_t bep_period;
|
||||
u_int8_t pfc_supported;
|
||||
u_int8_t dtm_supported;
|
||||
u_int8_t bss_paging_coordination;
|
||||
} ext_info;
|
||||
};
|
||||
|
||||
/* TS 04.60 Table 12.9.2 */
|
||||
|
||||
@@ -42,6 +42,7 @@ enum signal_subsystems {
|
||||
SS_SUBSCR,
|
||||
SS_SCALL,
|
||||
SS_GLOBAL,
|
||||
SS_CHALLOC,
|
||||
};
|
||||
|
||||
/* SS_PAGING signals */
|
||||
@@ -93,6 +94,12 @@ enum signal_lchan {
|
||||
S_LCHAN_MEAS_REP, /* 08.58 Measurement Report */
|
||||
};
|
||||
|
||||
/* SS_CHALLOC signals */
|
||||
enum signal_challoc {
|
||||
S_CHALLOC_ALLOC_FAIL, /* allocation of lchan has failed */
|
||||
S_CHALLOC_FREED, /* lchan has been successfully freed */
|
||||
};
|
||||
|
||||
/* SS_SUBSCR signals */
|
||||
enum signal_subscr {
|
||||
S_SUBSCR_ATTACHED,
|
||||
@@ -130,4 +137,10 @@ struct ipacc_ack_signal_data {
|
||||
u_int8_t msg_type;
|
||||
};
|
||||
|
||||
struct challoc_signal_data {
|
||||
struct gsm_bts *bts;
|
||||
struct gsm_lchan *lchan;
|
||||
enum gsm_chan_t type;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#define TELNET_INTERFACE_H
|
||||
|
||||
#include "gsm_data.h"
|
||||
#include "debug.h"
|
||||
#include <openbsc/debug.h>
|
||||
#include <osmocore/select.h>
|
||||
|
||||
#include <vty/vty.h>
|
||||
@@ -32,7 +32,7 @@ struct telnet_connection {
|
||||
struct gsm_network *network;
|
||||
struct bsc_fd fd;
|
||||
struct vty *vty;
|
||||
struct debug_target *dbg;
|
||||
struct log_target *dbg;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ struct gsm_trans {
|
||||
/* To whom we belong, unique identifier of remote MM entity */
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
/* The LCHAN that we're currently using to transmit messages */
|
||||
struct gsm_lchan *lchan;
|
||||
/* The associated connection we are using to transmit messages */
|
||||
struct gsm_subscriber_connection *conn;
|
||||
|
||||
/* reference from MNCC or other application */
|
||||
u_int32_t callref;
|
||||
@@ -71,6 +71,6 @@ int trans_assign_trans_id(struct gsm_subscriber *subscr,
|
||||
|
||||
/* update all transactions to use a different LCHAN, e.g.
|
||||
* after handover has succeeded */
|
||||
int trans_lchan_change(struct gsm_lchan *lchan_old,
|
||||
struct gsm_lchan *lchan_new);
|
||||
int trans_lchan_change(struct gsm_subscriber_connection *conn_old,
|
||||
struct gsm_subscriber_connection *conn_new);
|
||||
#endif
|
||||
|
||||
10
openbsc/include/openbsc/vty.h
Normal file
10
openbsc/include/openbsc/vty.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef OPENBSC_VTY_H
|
||||
#define OPENBSC_VTY_H
|
||||
|
||||
struct gsm_network;
|
||||
struct vty;
|
||||
|
||||
void openbsc_vty_add_cmds(void);
|
||||
void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *);
|
||||
|
||||
#endif
|
||||
@@ -94,7 +94,7 @@ struct sccp_connection {
|
||||
* call sccp_system_incoming for incoming data (from the network)
|
||||
* sccp will call outgoing whenever outgoing data exists
|
||||
*/
|
||||
int sccp_system_init(int (*outgoing)(struct msgb *data, void *ctx), void *context);
|
||||
int sccp_system_init(void (*outgoing)(struct msgb *data, void *ctx), void *context);
|
||||
int sccp_system_incoming(struct msgb *data);
|
||||
|
||||
/**
|
||||
@@ -105,6 +105,11 @@ int sccp_connection_send_it(struct sccp_connection *connection);
|
||||
int sccp_connection_close(struct sccp_connection *connection, int cause);
|
||||
int sccp_connection_free(struct sccp_connection *connection);
|
||||
|
||||
/**
|
||||
* internal..
|
||||
*/
|
||||
int sccp_connection_force_free(struct sccp_connection *conn);
|
||||
|
||||
/**
|
||||
* Create a new socket. Set your callbacks and then call bind to open
|
||||
* the connection.
|
||||
|
||||
@@ -411,4 +411,10 @@ struct sccp_data_it {
|
||||
u_int8_t credit;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sccp_proto_err {
|
||||
u_int8_t type;
|
||||
struct sccp_source_reference destination_local_reference;
|
||||
u_int8_t error_cause;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
/* Create a new buffer. Memory will be allocated in chunks of the given
|
||||
size. If the argument is 0, the library will supply a reasonable
|
||||
default size suitable for buffering socket I/O. */
|
||||
struct buffer *buffer_new(size_t);
|
||||
struct buffer *buffer_new(void *ctx, size_t);
|
||||
|
||||
/* Free all data in the buffer. */
|
||||
void buffer_reset(struct buffer *);
|
||||
|
||||
@@ -107,6 +107,8 @@ enum node_type {
|
||||
TS_NODE,
|
||||
SUBSCR_NODE,
|
||||
MGCP_NODE,
|
||||
NAT_NODE,
|
||||
BSC_NODE,
|
||||
};
|
||||
|
||||
/* Node which has some commands and prompt string and configuration
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
|
||||
AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS)
|
||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS)
|
||||
|
||||
sbin_PROGRAMS = bsc_hack bs11_config ipaccess-find ipaccess-config \
|
||||
isdnsync bsc_mgcp ipaccess-proxy
|
||||
isdnsync bsc_mgcp ipaccess-proxy \
|
||||
bsc_msc_ip bsc_nat
|
||||
noinst_LIBRARIES = libbsc.a libmsc.a libvty.a
|
||||
noinst_HEADERS = vty/cardshell.h
|
||||
|
||||
@@ -17,12 +18,12 @@ libbsc_a_SOURCES = abis_rsl.c abis_nm.c gsm_data.c gsm_04_08_utils.c \
|
||||
input/misdn.c input/ipaccess.c \
|
||||
talloc_ctx.c system_information.c rest_octets.c \
|
||||
rtp_proxy.c bts_siemens_bs11.c bts_ipaccess_nanobts.c \
|
||||
bts_unknown.c
|
||||
bts_unknown.c meas_rep.c telnet_interface.c bsc_version.c bsc_api.c vty_interface_cmds.c
|
||||
|
||||
libmsc_a_SOURCES = gsm_subscriber.c db.c telnet_interface.c \
|
||||
libmsc_a_SOURCES = gsm_subscriber.c db.c \
|
||||
mncc.c gsm_04_08.c gsm_04_11.c transaction.c \
|
||||
token_auth.c rrlp.c gsm_04_80.c ussd.c silent_call.c \
|
||||
handover_logic.c handover_decision.c meas_rep.c
|
||||
handover_logic.c handover_decision.c
|
||||
|
||||
libvty_a_SOURCES = vty/buffer.c vty/command.c vty/vector.c vty/vty.c
|
||||
|
||||
@@ -33,6 +34,10 @@ bsc_hack_LDADD = libmsc.a libbsc.a libmsc.a libvty.a -ldl -ldbi $(LIBCRYPT)
|
||||
|
||||
bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c debug.c \
|
||||
rs232.c bts_siemens_bs11.c
|
||||
bsc_msc_ip_SOURCES = bssap.c bsc_msc_ip.c bsc_init.c vty_interface.c vty_interface_bsc.c \
|
||||
bsc_msc.c bsc_msc_rf.c
|
||||
bsc_msc_ip_LDADD = libbsc.a libvty.a libsccp.a
|
||||
|
||||
|
||||
ipaccess_find_SOURCES = ipaccess/ipaccess-find.c
|
||||
|
||||
@@ -42,7 +47,13 @@ ipaccess_config_LDADD = libbsc.a libmsc.a libbsc.a libvty.a -ldl -ldbi $(LIBCRYP
|
||||
isdnsync_SOURCES = isdnsync.c
|
||||
|
||||
bsc_mgcp_SOURCES = mgcp/mgcp_main.c mgcp/mgcp_protocol.c mgcp/mgcp_network.c mgcp/mgcp_vty.c \
|
||||
debug.c telnet_interface.c
|
||||
debug.c telnet_interface.c vty_interface_cmds.c
|
||||
bsc_mgcp_LDADD = libvty.a
|
||||
|
||||
ipaccess_proxy_SOURCES = ipaccess/ipaccess-proxy.c debug.c
|
||||
|
||||
bsc_nat_SOURCES = nat/bsc_nat.c nat/bsc_filter.c nat/bsc_sccp.c \
|
||||
nat/bsc_nat_utils.c nat/bsc_nat_vty.c nat/bsc_mgcp_utils.c \
|
||||
mgcp/mgcp_protocol.c mgcp/mgcp_network.c mgcp/mgcp_vty.c \
|
||||
bsc_msc.c bssap.c
|
||||
bsc_nat_LDADD = libvty.a libbsc.a libsccp.a
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* GSM Network Management (OML) messages on the A-bis interface
|
||||
/* GSM Network Management (OML) messages on the A-bis interface
|
||||
* 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
|
||||
|
||||
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
|
||||
@@ -114,125 +114,117 @@ static const enum abis_nm_msgtype nacks[] = {
|
||||
NM_MT_BS11_DELETE_OBJ_NACK,
|
||||
};
|
||||
|
||||
static const char *nack_names[0xff] = {
|
||||
[NM_MT_LOAD_INIT_NACK] = "SOFTWARE LOAD INIT",
|
||||
[NM_MT_LOAD_END_NACK] = "SOFTWARE LOAD END",
|
||||
[NM_MT_SW_ACT_REQ_NACK] = "SOFTWARE ACTIVATE REQUEST",
|
||||
[NM_MT_ACTIVATE_SW_NACK] = "ACTIVATE SOFTWARE",
|
||||
[NM_MT_ESTABLISH_TEI_NACK] = "ESTABLISH TEI",
|
||||
[NM_MT_CONN_TERR_SIGN_NACK] = "CONNECT TERRESTRIAL SIGNALLING",
|
||||
[NM_MT_DISC_TERR_SIGN_NACK] = "DISCONNECT TERRESTRIAL SIGNALLING",
|
||||
[NM_MT_CONN_TERR_TRAF_NACK] = "CONNECT TERRESTRIAL TRAFFIC",
|
||||
[NM_MT_DISC_TERR_TRAF_NACK] = "DISCONNECT TERRESTRIAL TRAFFIC",
|
||||
[NM_MT_CONN_MDROP_LINK_NACK] = "CONNECT MULTI-DROP LINK",
|
||||
[NM_MT_DISC_MDROP_LINK_NACK] = "DISCONNECT MULTI-DROP LINK",
|
||||
[NM_MT_SET_BTS_ATTR_NACK] = "SET BTS ATTRIBUTE",
|
||||
[NM_MT_SET_RADIO_ATTR_NACK] = "SET RADIO ATTRIBUTE",
|
||||
[NM_MT_SET_CHAN_ATTR_NACK] = "SET CHANNEL ATTRIBUTE",
|
||||
[NM_MT_PERF_TEST_NACK] = "PERFORM TEST",
|
||||
[NM_MT_SEND_TEST_REP_NACK] = "SEND TEST REPORT",
|
||||
[NM_MT_STOP_TEST_NACK] = "STOP TEST",
|
||||
[NM_MT_STOP_EVENT_REP_NACK] = "STOP EVENT REPORT",
|
||||
[NM_MT_REST_EVENT_REP_NACK] = "RESET EVENT REPORT",
|
||||
[NM_MT_CHG_ADM_STATE_NACK] = "CHANGE ADMINISTRATIVE STATE",
|
||||
[NM_MT_CHG_ADM_STATE_REQ_NACK] = "CHANGE ADMINISTRATIVE STATE REQUEST",
|
||||
[NM_MT_REP_OUTST_ALARMS_NACK] = "REPORT OUTSTANDING ALARMS",
|
||||
[NM_MT_CHANGEOVER_NACK] = "CHANGEOVER",
|
||||
[NM_MT_OPSTART_NACK] = "OPSTART",
|
||||
[NM_MT_REINIT_NACK] = "REINIT",
|
||||
[NM_MT_SET_SITE_OUT_NACK] = "SET SITE OUTPUT",
|
||||
[NM_MT_CHG_HW_CONF_NACK] = "CHANGE HARDWARE CONFIGURATION",
|
||||
[NM_MT_GET_ATTR_NACK] = "GET ATTRIBUTE",
|
||||
[NM_MT_SET_ALARM_THRES_NACK] = "SET ALARM THRESHOLD",
|
||||
[NM_MT_BS11_BEGIN_DB_TX_NACK] = "BS11 BEGIN DATABASE TRANSMISSION",
|
||||
[NM_MT_BS11_END_DB_TX_NACK] = "BS11 END DATABASE TRANSMISSION",
|
||||
[NM_MT_BS11_CREATE_OBJ_NACK] = "BS11 CREATE OBJECT",
|
||||
[NM_MT_BS11_DELETE_OBJ_NACK] = "BS11 DELETE OBJECT",
|
||||
static const struct value_string nack_names[] = {
|
||||
{ NM_MT_LOAD_INIT_NACK, "SOFTWARE LOAD INIT" },
|
||||
{ NM_MT_LOAD_END_NACK, "SOFTWARE LOAD END" },
|
||||
{ NM_MT_SW_ACT_REQ_NACK, "SOFTWARE ACTIVATE REQUEST" },
|
||||
{ NM_MT_ACTIVATE_SW_NACK, "ACTIVATE SOFTWARE" },
|
||||
{ NM_MT_ESTABLISH_TEI_NACK, "ESTABLISH TEI" },
|
||||
{ NM_MT_CONN_TERR_SIGN_NACK, "CONNECT TERRESTRIAL SIGNALLING" },
|
||||
{ NM_MT_DISC_TERR_SIGN_NACK, "DISCONNECT TERRESTRIAL SIGNALLING" },
|
||||
{ NM_MT_CONN_TERR_TRAF_NACK, "CONNECT TERRESTRIAL TRAFFIC" },
|
||||
{ NM_MT_DISC_TERR_TRAF_NACK, "DISCONNECT TERRESTRIAL TRAFFIC" },
|
||||
{ NM_MT_CONN_MDROP_LINK_NACK, "CONNECT MULTI-DROP LINK" },
|
||||
{ NM_MT_DISC_MDROP_LINK_NACK, "DISCONNECT MULTI-DROP LINK" },
|
||||
{ NM_MT_SET_BTS_ATTR_NACK, "SET BTS ATTRIBUTE" },
|
||||
{ NM_MT_SET_RADIO_ATTR_NACK, "SET RADIO ATTRIBUTE" },
|
||||
{ NM_MT_SET_CHAN_ATTR_NACK, "SET CHANNEL ATTRIBUTE" },
|
||||
{ NM_MT_PERF_TEST_NACK, "PERFORM TEST" },
|
||||
{ NM_MT_SEND_TEST_REP_NACK, "SEND TEST REPORT" },
|
||||
{ NM_MT_STOP_TEST_NACK, "STOP TEST" },
|
||||
{ NM_MT_STOP_EVENT_REP_NACK, "STOP EVENT REPORT" },
|
||||
{ NM_MT_REST_EVENT_REP_NACK, "RESET EVENT REPORT" },
|
||||
{ NM_MT_CHG_ADM_STATE_NACK, "CHANGE ADMINISTRATIVE STATE" },
|
||||
{ NM_MT_CHG_ADM_STATE_REQ_NACK,
|
||||
"CHANGE ADMINISTRATIVE STATE REQUEST" },
|
||||
{ NM_MT_REP_OUTST_ALARMS_NACK, "REPORT OUTSTANDING ALARMS" },
|
||||
{ NM_MT_CHANGEOVER_NACK, "CHANGEOVER" },
|
||||
{ NM_MT_OPSTART_NACK, "OPSTART" },
|
||||
{ NM_MT_REINIT_NACK, "REINIT" },
|
||||
{ NM_MT_SET_SITE_OUT_NACK, "SET SITE OUTPUT" },
|
||||
{ NM_MT_CHG_HW_CONF_NACK, "CHANGE HARDWARE CONFIGURATION" },
|
||||
{ NM_MT_GET_ATTR_NACK, "GET ATTRIBUTE" },
|
||||
{ NM_MT_SET_ALARM_THRES_NACK, "SET ALARM THRESHOLD" },
|
||||
{ NM_MT_BS11_BEGIN_DB_TX_NACK, "BS11 BEGIN DATABASE TRANSMISSION" },
|
||||
{ NM_MT_BS11_END_DB_TX_NACK, "BS11 END DATABASE TRANSMISSION" },
|
||||
{ NM_MT_BS11_CREATE_OBJ_NACK, "BS11 CREATE OBJECT" },
|
||||
{ NM_MT_BS11_DELETE_OBJ_NACK, "BS11 DELETE OBJECT" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
/* Chapter 9.4.36 */
|
||||
static const char *nack_cause_names[] = {
|
||||
static const struct value_string nack_cause_names[] = {
|
||||
/* General Nack Causes */
|
||||
[NM_NACK_INCORR_STRUCT] = "Incorrect message structure",
|
||||
[NM_NACK_MSGTYPE_INVAL] = "Invalid message type value",
|
||||
[NM_NACK_OBJCLASS_INVAL] = "Invalid Object class value",
|
||||
[NM_NACK_OBJCLASS_NOTSUPP] = "Object class not supported",
|
||||
[NM_NACK_BTSNR_UNKN] = "BTS no. unknown",
|
||||
[NM_NACK_TRXNR_UNKN] = "Baseband Transceiver no. unknown",
|
||||
[NM_NACK_OBJINST_UNKN] = "Object Instance unknown",
|
||||
[NM_NACK_ATTRID_INVAL] = "Invalid attribute identifier value",
|
||||
[NM_NACK_ATTRID_NOTSUPP] = "Attribute identifier not supported",
|
||||
[NM_NACK_PARAM_RANGE] = "Parameter value outside permitted range",
|
||||
[NM_NACK_ATTRLIST_INCONSISTENT] = "Inconsistency in attribute list",
|
||||
[NM_NACK_SPEC_IMPL_NOTSUPP] = "Specified implementation not supported",
|
||||
[NM_NACK_CANT_PERFORM] = "Message cannot be performed",
|
||||
{ NM_NACK_INCORR_STRUCT, "Incorrect message structure" },
|
||||
{ NM_NACK_MSGTYPE_INVAL, "Invalid message type value" },
|
||||
{ NM_NACK_OBJCLASS_INVAL, "Invalid Object class value" },
|
||||
{ NM_NACK_OBJCLASS_NOTSUPP, "Object class not supported" },
|
||||
{ NM_NACK_BTSNR_UNKN, "BTS no. unknown" },
|
||||
{ NM_NACK_TRXNR_UNKN, "Baseband Transceiver no. unknown" },
|
||||
{ NM_NACK_OBJINST_UNKN, "Object Instance unknown" },
|
||||
{ NM_NACK_ATTRID_INVAL, "Invalid attribute identifier value" },
|
||||
{ NM_NACK_ATTRID_NOTSUPP, "Attribute identifier not supported" },
|
||||
{ NM_NACK_PARAM_RANGE, "Parameter value outside permitted range" },
|
||||
{ NM_NACK_ATTRLIST_INCONSISTENT,"Inconsistency in attribute list" },
|
||||
{ NM_NACK_SPEC_IMPL_NOTSUPP, "Specified implementation not supported" },
|
||||
{ NM_NACK_CANT_PERFORM, "Message cannot be performed" },
|
||||
/* Specific Nack Causes */
|
||||
[NM_NACK_RES_NOTIMPL] = "Resource not implemented",
|
||||
[NM_NACK_RES_NOTAVAIL] = "Resource not available",
|
||||
[NM_NACK_FREQ_NOTAVAIL] = "Frequency not available",
|
||||
[NM_NACK_TEST_NOTSUPP] = "Test not supported",
|
||||
[NM_NACK_CAPACITY_RESTR] = "Capacity restrictions",
|
||||
[NM_NACK_PHYSCFG_NOTPERFORM] = "Physical configuration cannot be performed",
|
||||
[NM_NACK_TEST_NOTINIT] = "Test not initiated",
|
||||
[NM_NACK_PHYSCFG_NOTRESTORE] = "Physical configuration cannot be restored",
|
||||
[NM_NACK_TEST_NOSUCH] = "No such test",
|
||||
[NM_NACK_TEST_NOSTOP] = "Test cannot be stopped",
|
||||
[NM_NACK_MSGINCONSIST_PHYSCFG] = "Message inconsistent with physical configuration",
|
||||
[NM_NACK_FILE_INCOMPLETE] = "Complete file notreceived",
|
||||
[NM_NACK_FILE_NOTAVAIL] = "File not available at destination",
|
||||
[NM_NACK_FILE_NOTACTIVATE] = "File cannot be activate",
|
||||
[NM_NACK_REQ_NOT_GRANT] = "Request not granted",
|
||||
[NM_NACK_WAIT] = "Wait",
|
||||
[NM_NACK_NOTH_REPORT_EXIST] = "Nothing reportable existing",
|
||||
[NM_NACK_MEAS_NOTSUPP] = "Measurement not supported",
|
||||
[NM_NACK_MEAS_NOTSTART] = "Measurement not started",
|
||||
{ NM_NACK_RES_NOTIMPL, "Resource not implemented" },
|
||||
{ NM_NACK_RES_NOTAVAIL, "Resource not available" },
|
||||
{ NM_NACK_FREQ_NOTAVAIL, "Frequency not available" },
|
||||
{ NM_NACK_TEST_NOTSUPP, "Test not supported" },
|
||||
{ NM_NACK_CAPACITY_RESTR, "Capacity restrictions" },
|
||||
{ NM_NACK_PHYSCFG_NOTPERFORM, "Physical configuration cannot be performed" },
|
||||
{ NM_NACK_TEST_NOTINIT, "Test not initiated" },
|
||||
{ NM_NACK_PHYSCFG_NOTRESTORE, "Physical configuration cannot be restored" },
|
||||
{ NM_NACK_TEST_NOSUCH, "No such test" },
|
||||
{ NM_NACK_TEST_NOSTOP, "Test cannot be stopped" },
|
||||
{ NM_NACK_MSGINCONSIST_PHYSCFG, "Message inconsistent with physical configuration" },
|
||||
{ NM_NACK_FILE_INCOMPLETE, "Complete file notreceived" },
|
||||
{ NM_NACK_FILE_NOTAVAIL, "File not available at destination" },
|
||||
{ NM_NACK_FILE_NOTACTIVATE, "File cannot be activate" },
|
||||
{ NM_NACK_REQ_NOT_GRANT, "Request not granted" },
|
||||
{ NM_NACK_WAIT, "Wait" },
|
||||
{ NM_NACK_NOTH_REPORT_EXIST, "Nothing reportable existing" },
|
||||
{ NM_NACK_MEAS_NOTSUPP, "Measurement not supported" },
|
||||
{ NM_NACK_MEAS_NOTSTART, "Measurement not started" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static char namebuf[255];
|
||||
static const char *nack_cause_name(u_int8_t cause)
|
||||
{
|
||||
if (cause < ARRAY_SIZE(nack_cause_names) && nack_cause_names[cause])
|
||||
return nack_cause_names[cause];
|
||||
|
||||
snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause);
|
||||
return namebuf;
|
||||
return get_value_string(nack_cause_names, cause);
|
||||
}
|
||||
|
||||
/* Chapter 9.4.16: Event Type */
|
||||
static const char *event_type_names[] = {
|
||||
[NM_EVT_COMM_FAIL] = "communication failure",
|
||||
[NM_EVT_QOS_FAIL] = "quality of service failure",
|
||||
[NM_EVT_PROC_FAIL] = "processing failure",
|
||||
[NM_EVT_EQUIP_FAIL] = "equipment failure",
|
||||
[NM_EVT_ENV_FAIL] = "environment failure",
|
||||
static const struct value_string event_type_names[] = {
|
||||
{ NM_EVT_COMM_FAIL, "communication failure" },
|
||||
{ NM_EVT_QOS_FAIL, "quality of service failure" },
|
||||
{ NM_EVT_PROC_FAIL, "processing failure" },
|
||||
{ NM_EVT_EQUIP_FAIL, "equipment failure" },
|
||||
{ NM_EVT_ENV_FAIL, "environment failure" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const char *event_type_name(u_int8_t cause)
|
||||
{
|
||||
if (cause < ARRAY_SIZE(event_type_names) && event_type_names[cause])
|
||||
return event_type_names[cause];
|
||||
|
||||
snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause);
|
||||
return namebuf;
|
||||
return get_value_string(event_type_names, cause);
|
||||
}
|
||||
|
||||
/* Chapter 9.4.63: Perceived Severity */
|
||||
static const char *severity_names[] = {
|
||||
[NM_SEVER_CEASED] = "failure ceased",
|
||||
[NM_SEVER_CRITICAL] = "critical failure",
|
||||
[NM_SEVER_MAJOR] = "major failure",
|
||||
[NM_SEVER_MINOR] = "minor failure",
|
||||
[NM_SEVER_WARNING] = "warning level failure",
|
||||
[NM_SEVER_INDETERMINATE] = "indeterminate failure",
|
||||
static const struct value_string severity_names[] = {
|
||||
{ NM_SEVER_CEASED, "failure ceased" },
|
||||
{ NM_SEVER_CRITICAL, "critical failure" },
|
||||
{ NM_SEVER_MAJOR, "major failure" },
|
||||
{ NM_SEVER_MINOR, "minor failure" },
|
||||
{ NM_SEVER_WARNING, "warning level failure" },
|
||||
{ NM_SEVER_INDETERMINATE, "indeterminate failure" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const char *severity_name(u_int8_t cause)
|
||||
{
|
||||
if (cause < ARRAY_SIZE(severity_names) && severity_names[cause])
|
||||
return severity_names[cause];
|
||||
|
||||
snprintf(namebuf, sizeof(namebuf), "0x%02x\n", cause);
|
||||
return namebuf;
|
||||
return get_value_string(severity_names, cause);
|
||||
}
|
||||
|
||||
/* Attributes that the BSC can set, not only get, according to Section 9.4 */
|
||||
@@ -427,46 +419,30 @@ int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg)
|
||||
|
||||
static int abis_nm_rcvmsg_sw(struct msgb *mb);
|
||||
|
||||
static struct value_string obj_class_names[] = {
|
||||
{ NM_OC_SITE_MANAGER, "SITE MANAGER" },
|
||||
{ NM_OC_BTS, "BTS" },
|
||||
{ NM_OC_RADIO_CARRIER, "RADIO CARRIER" },
|
||||
{ NM_OC_BASEB_TRANSC, "BASEBAND TRANSCEIVER" },
|
||||
{ NM_OC_CHANNEL, "CHANNEL" },
|
||||
{ NM_OC_BS11_ADJC, "ADJC" },
|
||||
{ NM_OC_BS11_HANDOVER, "HANDOVER" },
|
||||
{ NM_OC_BS11_PWR_CTRL, "POWER CONTROL" },
|
||||
{ NM_OC_BS11_BTSE, "BTSE" },
|
||||
{ NM_OC_BS11_RACK, "RACK" },
|
||||
{ NM_OC_BS11_TEST, "TEST" },
|
||||
{ NM_OC_BS11_ENVABTSE, "ENVABTSE" },
|
||||
{ NM_OC_BS11_BPORT, "BPORT" },
|
||||
{ NM_OC_GPRS_NSE, "GPRS NSE" },
|
||||
{ NM_OC_GPRS_CELL, "GPRS CELL" },
|
||||
{ NM_OC_GPRS_NSVC, "GPRS NSVC" },
|
||||
{ NM_OC_BS11, "SIEMENSHW" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
static const char *obj_class_name(u_int8_t oc)
|
||||
{
|
||||
switch (oc) {
|
||||
case NM_OC_SITE_MANAGER:
|
||||
return "SITE MANAGER";
|
||||
case NM_OC_BTS:
|
||||
return "BTS";
|
||||
case NM_OC_RADIO_CARRIER:
|
||||
return "RADIO CARRIER";
|
||||
case NM_OC_BASEB_TRANSC:
|
||||
return "BASEBAND TRANSCEIVER";
|
||||
case NM_OC_CHANNEL:
|
||||
return "CHANNEL";
|
||||
case NM_OC_BS11_ADJC:
|
||||
return "ADJC";
|
||||
case NM_OC_BS11_HANDOVER:
|
||||
return "HANDOVER";
|
||||
case NM_OC_BS11_PWR_CTRL:
|
||||
return "POWER CONTROL";
|
||||
case NM_OC_BS11_BTSE:
|
||||
return "BTSE";
|
||||
case NM_OC_BS11_RACK:
|
||||
return "RACK";
|
||||
case NM_OC_BS11_TEST:
|
||||
return "TEST";
|
||||
case NM_OC_BS11_ENVABTSE:
|
||||
return "ENVABTSE";
|
||||
case NM_OC_BS11_BPORT:
|
||||
return "BPORT";
|
||||
case NM_OC_GPRS_NSE:
|
||||
return "GPRS NSE";
|
||||
case NM_OC_GPRS_CELL:
|
||||
return "GPRS CELL";
|
||||
case NM_OC_GPRS_NSVC:
|
||||
return "GPRS NSVC";
|
||||
case NM_OC_BS11:
|
||||
return "SIEMENSHW";
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
return get_value_string(obj_class_names, oc);
|
||||
}
|
||||
|
||||
const char *nm_opstate_name(u_int8_t os)
|
||||
@@ -484,24 +460,22 @@ const char *nm_opstate_name(u_int8_t os)
|
||||
}
|
||||
|
||||
/* Chapter 9.4.7 */
|
||||
static const char *avail_names[] = {
|
||||
"In test",
|
||||
"Failed",
|
||||
"Power off",
|
||||
"Off line",
|
||||
"<not used>",
|
||||
"Dependency",
|
||||
"Degraded",
|
||||
"Not installed",
|
||||
static const struct value_string avail_names[] = {
|
||||
{ 0, "In test" },
|
||||
{ 1, "Failed" },
|
||||
{ 2, "Power off" },
|
||||
{ 3, "Off line" },
|
||||
/* Not used */
|
||||
{ 5, "Dependency" },
|
||||
{ 6, "Degraded" },
|
||||
{ 7, "Not installed" },
|
||||
{ 0xff, "OK" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *nm_avail_name(u_int8_t avail)
|
||||
{
|
||||
if (avail == 0xff)
|
||||
return "OK";
|
||||
if (avail >= ARRAY_SIZE(avail_names))
|
||||
return "UNKNOWN";
|
||||
return avail_names[avail];
|
||||
return get_value_string(avail_names, avail);
|
||||
}
|
||||
|
||||
static struct value_string test_names[] = {
|
||||
@@ -540,7 +514,7 @@ int nm_is_running(struct gsm_nm_state *s) {
|
||||
static void debugp_foh(struct abis_om_fom_hdr *foh)
|
||||
{
|
||||
DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ",
|
||||
obj_class_name(foh->obj_class), foh->obj_class,
|
||||
obj_class_name(foh->obj_class), foh->obj_class,
|
||||
foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
|
||||
foh->obj_inst.ts_nr);
|
||||
}
|
||||
@@ -848,15 +822,56 @@ static int ipacc_sw_activate(struct gsm_bts *bts, u_int8_t obj_class, u_int8_t i
|
||||
return abis_nm_sendmsg(bts, msg);
|
||||
}
|
||||
|
||||
static int abis_nm_parse_sw_descr(const u_int8_t *sw_descr, int sw_descr_len)
|
||||
{
|
||||
static const struct tlv_definition sw_descr_def = {
|
||||
.def = {
|
||||
[NM_ATT_FILE_ID] = { TLV_TYPE_TL16V, },
|
||||
[NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V, },
|
||||
},
|
||||
};
|
||||
|
||||
u_int8_t tag;
|
||||
u_int16_t tag_len;
|
||||
const u_int8_t *val;
|
||||
int ofs = 0, len;
|
||||
|
||||
/* Classic TLV parsing doesn't work well with SW_DESCR because of it's
|
||||
* nested nature and the fact you have to assume it contains only two sub
|
||||
* tags NM_ATT_FILE_VERSION & NM_ATT_FILE_ID to parse it */
|
||||
|
||||
if (sw_descr[0] != NM_ATT_SW_DESCR) {
|
||||
DEBUGP(DNM, "SW_DESCR attribute identifier not found!\n");
|
||||
return -1;
|
||||
}
|
||||
ofs += 1;
|
||||
|
||||
len = tlv_parse_one(&tag, &tag_len, &val,
|
||||
&sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs);
|
||||
if (len < 0 || (tag != NM_ATT_FILE_ID)) {
|
||||
DEBUGP(DNM, "FILE_ID attribute identifier not found!\n");
|
||||
return -2;
|
||||
}
|
||||
ofs += len;
|
||||
|
||||
len = tlv_parse_one(&tag, &tag_len, &val,
|
||||
&sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs);
|
||||
if (len < 0 || (tag != NM_ATT_FILE_VERSION)) {
|
||||
DEBUGP(DNM, "FILE_VERSION attribute identifier not found!\n");
|
||||
return -3;
|
||||
}
|
||||
ofs += len;
|
||||
|
||||
return ofs;
|
||||
}
|
||||
|
||||
static int abis_nm_rx_sw_act_req(struct msgb *mb)
|
||||
{
|
||||
struct abis_om_hdr *oh = msgb_l2(mb);
|
||||
struct abis_om_fom_hdr *foh = msgb_l3(mb);
|
||||
struct tlv_parsed tp;
|
||||
const u_int8_t *sw_config;
|
||||
int sw_config_len;
|
||||
int file_id_len;
|
||||
int ret;
|
||||
int ret, sw_config_len, sw_descr_len;
|
||||
|
||||
debugp_foh(foh);
|
||||
|
||||
@@ -880,20 +895,16 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb)
|
||||
DEBUGP(DNM, "Found SW config: %s\n", hexdump(sw_config, sw_config_len));
|
||||
}
|
||||
|
||||
if (sw_config[0] != NM_ATT_SW_DESCR)
|
||||
DEBUGP(DNM, "SW_DESCR attribute identifier not found!\n");
|
||||
if (sw_config[1] != NM_ATT_FILE_ID)
|
||||
DEBUGP(DNM, "FILE_ID attribute identifier not found!\n");
|
||||
file_id_len = sw_config[2] * 256 + sw_config[3];
|
||||
/* Use the first SW_DESCR present in SW config */
|
||||
sw_descr_len = abis_nm_parse_sw_descr(sw_config, sw_config_len);
|
||||
if (sw_descr_len < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Assumes first SW file in list is the one to be activated */
|
||||
/* sw_config + 4 to skip over 2 attribute ID bytes and 16-bit length field */
|
||||
return ipacc_sw_activate(mb->trx->bts, foh->obj_class,
|
||||
foh->obj_inst.bts_nr,
|
||||
foh->obj_inst.trx_nr,
|
||||
foh->obj_inst.ts_nr,
|
||||
sw_config + 4,
|
||||
file_id_len);
|
||||
sw_config, sw_descr_len);
|
||||
}
|
||||
|
||||
/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */
|
||||
@@ -960,15 +971,11 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
|
||||
|
||||
debugp_foh(foh);
|
||||
|
||||
if (nack_names[mt])
|
||||
DEBUGPC(DNM, "%s NACK ", nack_names[mt]);
|
||||
/* FIXME: NACK cause */
|
||||
else
|
||||
DEBUGPC(DNM, "NACK 0x%02x ", mt);
|
||||
DEBUGPC(DNM, "%s NACK ", get_value_string(nack_names, mt));
|
||||
|
||||
abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh));
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
|
||||
DEBUGPC(DNM, "CAUSE=%s\n",
|
||||
DEBUGPC(DNM, "CAUSE=%s\n",
|
||||
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
|
||||
else
|
||||
DEBUGPC(DNM, "\n");
|
||||
@@ -1379,7 +1386,7 @@ static int sw_open_file(struct abis_nm_sw *sw, const char *fname)
|
||||
return -1;
|
||||
}
|
||||
/* read first line and parse file ID and VERSION */
|
||||
rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n",
|
||||
rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n",
|
||||
file_id, file_version);
|
||||
if (rc != 2) {
|
||||
perror("parsing header line of software file");
|
||||
@@ -2263,7 +2270,7 @@ int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts)
|
||||
}
|
||||
|
||||
/* like abis_nm_conn_terr_traf + set_tei */
|
||||
int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, u_int8_t e1_port,
|
||||
int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, u_int8_t e1_port,
|
||||
u_int8_t e1_timeslot, u_int8_t e1_subslot,
|
||||
u_int8_t tei)
|
||||
{
|
||||
@@ -2625,7 +2632,7 @@ static u_int8_t req_attr_btsm[] = {
|
||||
NM_ATT_SW_DESCR, NM_ATT_GET_ARI };
|
||||
#endif
|
||||
|
||||
static u_int8_t req_attr[] = {
|
||||
static u_int8_t req_attr[] = {
|
||||
NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE,
|
||||
0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO,
|
||||
0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL };
|
||||
@@ -2704,11 +2711,11 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
|
||||
DEBUGPC(DNM, "RSL CONNECT ACK ");
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP))
|
||||
DEBUGPC(DNM, "IP=%s ",
|
||||
inet_ntoa(*((struct in_addr *)
|
||||
inet_ntoa(*((struct in_addr *)
|
||||
TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP))));
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP_PORT))
|
||||
DEBUGPC(DNM, "PORT=%u ",
|
||||
ntohs(*((u_int16_t *)
|
||||
ntohs(*((u_int16_t *)
|
||||
TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT))));
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID))
|
||||
DEBUGPC(DNM, "STREAM=0x%02x ",
|
||||
@@ -2718,7 +2725,7 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
|
||||
case NM_MT_IPACC_RSL_CONNECT_NACK:
|
||||
LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK ");
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
|
||||
DEBUGPC(DNM, " CAUSE=%s\n",
|
||||
DEBUGPC(DNM, " CAUSE=%s\n",
|
||||
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
|
||||
else
|
||||
DEBUGPC(DNM, "\n");
|
||||
@@ -2730,7 +2737,7 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
|
||||
case NM_MT_IPACC_SET_NVATTR_NACK:
|
||||
LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK ");
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
|
||||
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
|
||||
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
|
||||
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
|
||||
else
|
||||
LOGPC(DNM, LOGL_ERROR, "\n");
|
||||
@@ -2742,7 +2749,7 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
|
||||
case NM_MT_IPACC_GET_NVATTR_NACK:
|
||||
LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK ");
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
|
||||
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
|
||||
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
|
||||
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
|
||||
else
|
||||
LOGPC(DNM, LOGL_ERROR, "\n");
|
||||
@@ -2753,7 +2760,7 @@ static int abis_nm_rx_ipacc(struct msgb *msg)
|
||||
case NM_MT_IPACC_SET_ATTR_NACK:
|
||||
LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK ");
|
||||
if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES))
|
||||
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
|
||||
LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n",
|
||||
nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES)));
|
||||
else
|
||||
LOGPC(DNM, LOGL_ERROR, "\n");
|
||||
@@ -2830,7 +2837,7 @@ int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, u_int8_t *attr,
|
||||
attr_len);
|
||||
}
|
||||
|
||||
int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
|
||||
int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx,
|
||||
u_int32_t ip, u_int16_t port, u_int8_t stream)
|
||||
{
|
||||
struct in_addr ia;
|
||||
@@ -2894,21 +2901,18 @@ void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked)
|
||||
new_state);
|
||||
}
|
||||
|
||||
static const char *ipacc_testres_names[] = {
|
||||
[NM_IPACC_TESTRES_SUCCESS] = "SUCCESS",
|
||||
[NM_IPACC_TESTRES_TIMEOUT] = "TIMEOUT",
|
||||
[NM_IPACC_TESTRES_NO_CHANS] = "NO CHANNELS",
|
||||
[NM_IPACC_TESTRES_PARTIAL] = "PARTIAL",
|
||||
[NM_IPACC_TESTRES_STOPPED] = "STOPPED",
|
||||
static const struct value_string ipacc_testres_names[] = {
|
||||
{ NM_IPACC_TESTRES_SUCCESS, "SUCCESS" },
|
||||
{ NM_IPACC_TESTRES_TIMEOUT, "TIMEOUT" },
|
||||
{ NM_IPACC_TESTRES_NO_CHANS, "NO CHANNELS" },
|
||||
{ NM_IPACC_TESTRES_PARTIAL, "PARTIAL" },
|
||||
{ NM_IPACC_TESTRES_STOPPED, "STOPPED" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *ipacc_testres_name(u_int8_t res)
|
||||
{
|
||||
if (res < ARRAY_SIZE(ipacc_testres_names) &&
|
||||
ipacc_testres_names[res])
|
||||
return ipacc_testres_names[res];
|
||||
|
||||
return "unknown";
|
||||
return get_value_string(ipacc_testres_names, res);
|
||||
}
|
||||
|
||||
void ipac_parse_cgi(struct cell_global_id *cid, const u_int8_t *buf)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* GSM Radio Signalling Link messages on the A-bis interface
|
||||
/* GSM Radio Signalling Link messages on the A-bis interface
|
||||
* 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
|
||||
|
||||
/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
|
||||
@@ -118,8 +118,8 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
|
||||
}
|
||||
|
||||
lchan = &ts->lchan[lch_idx];
|
||||
debug_set_context(BSC_CTX_LCHAN, lchan);
|
||||
debug_set_context(BSC_CTX_SUBSCR, lchan->subscr);
|
||||
log_set_context(BSC_CTX_LCHAN, lchan);
|
||||
log_set_context(BSC_CTX_SUBSCR, lchan->conn.subscr);
|
||||
|
||||
return lchan;
|
||||
}
|
||||
@@ -222,7 +222,7 @@ static void print_rsl_cause(int lvl, const u_int8_t *cause_v, u_int8_t cause_len
|
||||
|
||||
LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ",
|
||||
cause_v[0], rsl_err_name(cause_v[0]));
|
||||
for (i = 1; i < cause_len-1; i++)
|
||||
for (i = 1; i < cause_len-1; i++)
|
||||
LOGPC(DRSL, lvl, "%02x ", cause_v[i]);
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ int rsl_bcch_info(struct gsm_bts_trx *trx, u_int8_t type,
|
||||
return abis_rsl_sendmsg(msg);
|
||||
}
|
||||
|
||||
int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type,
|
||||
int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type,
|
||||
const u_int8_t *data, int len)
|
||||
{
|
||||
struct abis_rsl_common_hdr *ch;
|
||||
@@ -416,7 +416,7 @@ int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr,
|
||||
}
|
||||
#endif
|
||||
|
||||
int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
|
||||
int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type,
|
||||
u_int8_t ta, u_int8_t ho_ref)
|
||||
{
|
||||
struct abis_rsl_dchan_hdr *dh;
|
||||
@@ -568,12 +568,33 @@ int rsl_deact_sacch(struct gsm_lchan *lchan)
|
||||
return abis_rsl_sendmsg(msg);
|
||||
}
|
||||
|
||||
static void error_timeout_cb(void *data)
|
||||
{
|
||||
struct gsm_lchan *lchan = data;
|
||||
if (lchan->state != LCHAN_S_REL_ERR) {
|
||||
LOGP(DRSL, LOGL_ERROR, "%s error timeout but not in error state: %d\n",
|
||||
gsm_lchan_name(lchan), lchan->state);
|
||||
return;
|
||||
}
|
||||
|
||||
/* go back to the none state */
|
||||
LOGP(DRSL, LOGL_NOTICE, "%s is back in operation.\n", gsm_lchan_name(lchan));
|
||||
rsl_lchan_set_state(lchan, LCHAN_S_NONE);
|
||||
}
|
||||
|
||||
/* Chapter 8.4.14 / 4.7: Tell BTS to release the radio channel */
|
||||
int rsl_rf_chan_release(struct gsm_lchan *lchan)
|
||||
static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error)
|
||||
{
|
||||
struct abis_rsl_dchan_hdr *dh;
|
||||
struct msgb *msg = rsl_msgb_alloc();
|
||||
struct msgb *msg;
|
||||
|
||||
if (lchan->state == LCHAN_S_REL_ERR) {
|
||||
LOGP(DRSL, LOGL_NOTICE, "%s is in error state not sending release.\n",
|
||||
gsm_lchan_name(lchan));
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg = rsl_msgb_alloc();
|
||||
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
|
||||
init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL);
|
||||
dh->chan_nr = lchan2chan_nr(lchan);
|
||||
@@ -581,7 +602,20 @@ int rsl_rf_chan_release(struct gsm_lchan *lchan)
|
||||
msg->lchan = lchan;
|
||||
msg->trx = lchan->ts->trx;
|
||||
|
||||
DEBUGP(DRSL, "%s RF Channel Release CMD\n", gsm_lchan_name(lchan));
|
||||
DEBUGP(DRSL, "%s RF Channel Release CMD due error %d\n", gsm_lchan_name(lchan), error);
|
||||
|
||||
if (error) {
|
||||
/*
|
||||
* the nanoBTS sends RLL release indications after the channel release. This can
|
||||
* be a problem when we have reassigned the channel to someone else and then can
|
||||
* not figure out who used this channel.
|
||||
*/
|
||||
rsl_lchan_set_state(lchan, LCHAN_S_REL_ERR);
|
||||
lchan->error_timer.data = lchan;
|
||||
lchan->error_timer.cb = error_timeout_cb;
|
||||
bsc_schedule_timer(&lchan->error_timer,
|
||||
msg->trx->bts->network->T3111 + 2, 0);
|
||||
}
|
||||
|
||||
/* BTS will respond by RF CHAN REL ACK */
|
||||
return abis_rsl_sendmsg(msg);
|
||||
@@ -718,16 +752,16 @@ int rsl_establish_request(struct gsm_lchan *lchan, u_int8_t link_id)
|
||||
RELEASE CONFIRM, which we in turn use to trigger RSL CHANNEL RELEASE,
|
||||
which in turn is acknowledged by RSL CHANNEL RELEASE ACK, which calls
|
||||
lchan_free() */
|
||||
int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id)
|
||||
int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id, u_int8_t reason)
|
||||
{
|
||||
|
||||
struct msgb *msg;
|
||||
|
||||
msg = rsl_rll_simple(RSL_MT_REL_REQ, lchan2chan_nr(lchan),
|
||||
link_id, 0);
|
||||
msgb_tv_put(msg, RSL_IE_RELEASE_MODE, 0); /* normal release */
|
||||
/* 0 is normal release, 1 is local end */
|
||||
msgb_tv_put(msg, RSL_IE_RELEASE_MODE, reason);
|
||||
|
||||
lchan->state = LCHAN_S_REL_REQ;
|
||||
/* FIXME: start some timer in case we don't receive a REL ACK ? */
|
||||
|
||||
msg->trx = lchan->ts->trx;
|
||||
@@ -735,6 +769,12 @@ int rsl_release_request(struct gsm_lchan *lchan, u_int8_t link_id)
|
||||
return abis_rsl_sendmsg(msg);
|
||||
}
|
||||
|
||||
int rsl_lchan_set_state(struct gsm_lchan *lchan, int state)
|
||||
{
|
||||
lchan->state = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Chapter 8.4.2: Channel Activate Acknowledge */
|
||||
static int rsl_rx_chan_act_ack(struct msgb *msg)
|
||||
{
|
||||
@@ -749,7 +789,7 @@ static int rsl_rx_chan_act_ack(struct msgb *msg)
|
||||
LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n",
|
||||
gsm_lchan_name(msg->lchan),
|
||||
gsm_lchans_name(msg->lchan->state));
|
||||
msg->lchan->state = LCHAN_S_ACTIVE;
|
||||
rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE);
|
||||
|
||||
dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_ACK, msg->lchan);
|
||||
|
||||
@@ -775,10 +815,10 @@ static int rsl_rx_chan_act_nack(struct msgb *msg)
|
||||
print_rsl_cause(LOGL_ERROR, cause,
|
||||
TLVP_LEN(&tp, RSL_IE_CAUSE));
|
||||
if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC)
|
||||
msg->lchan->state = LCHAN_S_NONE;
|
||||
rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE);
|
||||
} else
|
||||
msg->lchan->state = LCHAN_S_NONE;
|
||||
|
||||
rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE);
|
||||
|
||||
LOGPC(DRSL, LOGL_ERROR, "\n");
|
||||
|
||||
dispatch_signal(SS_LCHAN, S_LCHAN_ACTIVATE_NACK, msg->lchan);
|
||||
@@ -805,7 +845,8 @@ static int rsl_rx_conn_fail(struct msgb *msg)
|
||||
|
||||
LOGPC(DRSL, LOGL_NOTICE, "\n");
|
||||
/* FIXME: only free it after channel release ACK */
|
||||
return rsl_rf_chan_release(msg->lchan);
|
||||
counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rf_fail);
|
||||
return rsl_rf_chan_release(msg->lchan, 1);
|
||||
}
|
||||
|
||||
static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru,
|
||||
@@ -977,11 +1018,14 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
|
||||
break;
|
||||
case RSL_MT_RF_CHAN_REL_ACK:
|
||||
DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", ts_name);
|
||||
if (msg->lchan->state != LCHAN_S_REL_REQ)
|
||||
if (msg->lchan->state != LCHAN_S_REL_REQ && msg->lchan->state != LCHAN_S_REL_ERR)
|
||||
LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n",
|
||||
gsm_lchan_name(msg->lchan),
|
||||
gsm_lchans_name(msg->lchan->state));
|
||||
msg->lchan->state = LCHAN_S_NONE;
|
||||
bsc_del_timer(&msg->lchan->T3111);
|
||||
/* we have an error timer pending to release that */
|
||||
if (msg->lchan->state != LCHAN_S_REL_ERR)
|
||||
rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE);
|
||||
lchan_free(msg->lchan);
|
||||
break;
|
||||
case RSL_MT_MODE_MODIFY_ACK:
|
||||
@@ -992,12 +1036,14 @@ static int abis_rsl_rx_dchan(struct msgb *msg)
|
||||
break;
|
||||
case RSL_MT_IPAC_PDCH_ACT_ACK:
|
||||
DEBUGPC(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name);
|
||||
msg->lchan->ts->flags |= TS_F_PDCH_MODE;
|
||||
break;
|
||||
case RSL_MT_IPAC_PDCH_ACT_NACK:
|
||||
LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name);
|
||||
break;
|
||||
case RSL_MT_IPAC_PDCH_DEACT_ACK:
|
||||
DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name);
|
||||
msg->lchan->ts->flags &= ~TS_F_PDCH_MODE;
|
||||
break;
|
||||
case RSL_MT_IPAC_PDCH_DEACT_NACK:
|
||||
LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name);
|
||||
@@ -1054,7 +1100,7 @@ static int abis_rsl_rx_trx(struct msgb *msg)
|
||||
//DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(msg->trx));
|
||||
break;
|
||||
case RSL_MT_OVERLOAD:
|
||||
/* indicate CCCH / ACCH / processor overload */
|
||||
/* indicate CCCH / ACCH / processor overload */
|
||||
LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n",
|
||||
gsm_trx_name(msg->trx));
|
||||
break;
|
||||
@@ -1071,7 +1117,15 @@ static void t3101_expired(void *data)
|
||||
{
|
||||
struct gsm_lchan *lchan = data;
|
||||
|
||||
rsl_rf_chan_release(lchan);
|
||||
rsl_rf_chan_release(lchan, 1);
|
||||
}
|
||||
|
||||
/* If T3111 expires, we will send the RF Channel Request */
|
||||
static void t3111_expired(void *data)
|
||||
{
|
||||
struct gsm_lchan *lchan = data;
|
||||
|
||||
rsl_rf_chan_release(lchan, 0);
|
||||
}
|
||||
|
||||
/* MS has requested a channel on the RACH */
|
||||
@@ -1086,6 +1140,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
|
||||
struct gsm_lchan *lchan;
|
||||
u_int8_t rqd_ta;
|
||||
int ret;
|
||||
int is_lu;
|
||||
|
||||
u_int16_t arfcn;
|
||||
u_int8_t ts_number, subch;
|
||||
@@ -1103,13 +1158,19 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
|
||||
|
||||
/* determine channel type (SDCCH/TCH_F/TCH_H) based on
|
||||
* request reference RA */
|
||||
lctype = get_ctype_by_chreq(bts, rqd_ref->ra, bts->network->neci);
|
||||
chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra, bts->network->neci);
|
||||
lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra);
|
||||
chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci);
|
||||
|
||||
counter_inc(bts->network->stats.chreq.total);
|
||||
|
||||
/*
|
||||
* We want LOCATION UPDATES to succeed and will assign a TCH
|
||||
* if we have no SDCCH available.
|
||||
*/
|
||||
is_lu = !!(chreq_reason == GSM_CHREQ_REASON_LOCATION_UPD);
|
||||
|
||||
/* check availability / allocate channel */
|
||||
lchan = lchan_alloc(bts, lctype);
|
||||
lchan = lchan_alloc(bts, lctype, is_lu);
|
||||
if (!lchan) {
|
||||
LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n",
|
||||
msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra);
|
||||
@@ -1122,7 +1183,7 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
|
||||
LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel "
|
||||
"in state %s\n", gsm_lchan_name(lchan),
|
||||
gsm_lchans_name(lchan->state));
|
||||
lchan->state = LCHAN_S_ACT_REQ;
|
||||
rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ);
|
||||
|
||||
ts_number = lchan->ts->nr;
|
||||
arfcn = lchan->ts->trx->arfcn;
|
||||
@@ -1179,6 +1240,10 @@ static int rsl_rx_ccch_load(struct msgb *msg)
|
||||
switch (rslh->data[0]) {
|
||||
case RSL_IE_PAGING_LOAD:
|
||||
pg_buf_space = rslh->data[1] << 8 | rslh->data[2];
|
||||
if (is_ipaccess_bts(msg->trx->bts) && pg_buf_space == 0xffff) {
|
||||
/* paging load below configured threshold, use 50 as default */
|
||||
pg_buf_space = 50;
|
||||
}
|
||||
paging_update_buffer_space(msg->trx->bts, pg_buf_space);
|
||||
break;
|
||||
case RSL_IE_RACH_LOAD:
|
||||
@@ -1234,17 +1299,45 @@ static int rsl_rx_rll_err_ind(struct msgb *msg)
|
||||
|
||||
LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s\n",
|
||||
gsm_lchan_name(msg->lchan),
|
||||
get_value_string(rsl_rlm_cause_strs, rlm_cause[1]));
|
||||
rsl_rlm_cause_name(rlm_cause[1]));
|
||||
|
||||
rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND);
|
||||
|
||||
if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED)
|
||||
return rsl_rf_chan_release(msg->lchan);
|
||||
if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED) {
|
||||
counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rll_err);
|
||||
return rsl_rf_chan_release(msg->lchan, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST
|
||||
static void rsl_handle_release(struct gsm_lchan *lchan)
|
||||
{
|
||||
int sapi;
|
||||
struct gsm_bts *bts;
|
||||
|
||||
/* maybe we have only brought down one RLL */
|
||||
if (lchan->state != LCHAN_S_REL_REQ)
|
||||
return;
|
||||
|
||||
for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) {
|
||||
if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED)
|
||||
continue;
|
||||
LOGP(DRSL, LOGL_NOTICE, "%s waiting for SAPI=%d to be released.\n",
|
||||
gsm_lchan_name(lchan), sapi);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* wait a bit to send the RF Channel Release */
|
||||
lchan->T3111.cb = t3111_expired;
|
||||
lchan->T3111.data = lchan;
|
||||
bts = lchan->ts->trx->bts;
|
||||
bsc_schedule_timer(&lchan->T3111, bts->network->T3111, 0);
|
||||
}
|
||||
|
||||
/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST
|
||||
0x02, 0x06,
|
||||
0x01, 0x20,
|
||||
0x02, 0x00,
|
||||
@@ -1264,7 +1357,7 @@ static int abis_rsl_rx_rll(struct msgb *msg)
|
||||
switch (rllh->c.msg_type) {
|
||||
case RSL_MT_DATA_IND:
|
||||
DEBUGPC(DRLL, "DATA INDICATION\n");
|
||||
if (msgb_l2len(msg) >
|
||||
if (msgb_l2len(msg) >
|
||||
sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
|
||||
rllh->data[0] == RSL_IE_L3_INFO) {
|
||||
msg->l3h = &rllh->data[3];
|
||||
@@ -1276,7 +1369,7 @@ static int abis_rsl_rx_rll(struct msgb *msg)
|
||||
/* lchan is established, stop T3101 */
|
||||
msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS;
|
||||
bsc_del_timer(&msg->lchan->T3101);
|
||||
if (msgb_l2len(msg) >
|
||||
if (msgb_l2len(msg) >
|
||||
sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) &&
|
||||
rllh->data[0] == RSL_IE_L3_INFO) {
|
||||
msg->l3h = &rllh->data[3];
|
||||
@@ -1295,20 +1388,16 @@ static int abis_rsl_rx_rll(struct msgb *msg)
|
||||
msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED;
|
||||
rll_indication(msg->lchan, rllh->link_id,
|
||||
BSC_RLLR_IND_REL_IND);
|
||||
/* we can now releae the channel on the BTS/Abis side */
|
||||
/* FIXME: officially we need to start T3111 and wait for
|
||||
* some grace period */
|
||||
rsl_rf_chan_release(msg->lchan);
|
||||
rsl_handle_release(msg->lchan);
|
||||
rsl_lchan_rll_release(msg->lchan, rllh->link_id);
|
||||
break;
|
||||
case RSL_MT_REL_CONF:
|
||||
/* BTS informs us of having received UA from MS,
|
||||
* in response to DISC that we've sent earlier */
|
||||
DEBUGPC(DRLL, "RELEASE CONFIRMATION\n");
|
||||
msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED;
|
||||
/* we can now releae the channel on the BTS/Abis side */
|
||||
/* FIXME: officially we need to start T3111 and wait for
|
||||
* some grace period */
|
||||
rsl_rf_chan_release(msg->lchan);
|
||||
rsl_handle_release(msg->lchan);
|
||||
rsl_lchan_rll_release(msg->lchan, rllh->link_id);
|
||||
break;
|
||||
case RSL_MT_ERROR_IND:
|
||||
rc = rsl_rx_rll_err_ind(msg);
|
||||
@@ -1328,31 +1417,11 @@ static u_int8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan)
|
||||
{
|
||||
switch (lchan->tch_mode) {
|
||||
case GSM48_CMODE_SPEECH_V1:
|
||||
switch (lchan->type) {
|
||||
case GSM_LCHAN_TCH_F:
|
||||
return 0x00;
|
||||
case GSM_LCHAN_TCH_H:
|
||||
return 0x03;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0x00;
|
||||
case GSM48_CMODE_SPEECH_EFR:
|
||||
switch (lchan->type) {
|
||||
case GSM_LCHAN_TCH_F:
|
||||
return 0x01;
|
||||
/* there's no half-rate EFR */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0x01;
|
||||
case GSM48_CMODE_SPEECH_AMR:
|
||||
switch (lchan->type) {
|
||||
case GSM_LCHAN_TCH_F:
|
||||
return 0x02;
|
||||
case GSM_LCHAN_TCH_H:
|
||||
return 0x05;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0x02;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1491,17 +1560,24 @@ int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan)
|
||||
int rsl_ipacc_pdch_activate(struct gsm_lchan *lchan, int act)
|
||||
{
|
||||
struct msgb *msg = rsl_msgb_alloc();
|
||||
struct abis_rsl_dchan_hdr *dh;
|
||||
u_int8_t msg_type;
|
||||
|
||||
if (act)
|
||||
msg_type = RSL_MT_IPAC_PDCH_ACT;
|
||||
else
|
||||
msg_type = RSL_MT_IPAC_PDCH_DEACT;
|
||||
|
||||
dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
|
||||
init_dchan_hdr(dh, RSL_MT_IPAC_PDCH_ACT);
|
||||
init_dchan_hdr(dh, msg_type);
|
||||
dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN;
|
||||
dh->chan_nr = lchan2chan_nr(lchan);
|
||||
|
||||
DEBUGP(DRSL, "%s IPAC_PDCH_ACT\n", gsm_lchan_name(lchan));
|
||||
DEBUGP(DRSL, "%s IPAC_PDCH_%sACT\n", gsm_lchan_name(lchan),
|
||||
act ? "" : "DE");
|
||||
|
||||
msg->trx = lchan->ts->trx;
|
||||
|
||||
@@ -1643,9 +1719,21 @@ static int abis_rsl_rx_ipacc(struct msgb *msg)
|
||||
/* Entry-point where L2 RSL from BTS enters */
|
||||
int abis_rsl_rcvmsg(struct msgb *msg)
|
||||
{
|
||||
struct abis_rsl_common_hdr *rslh = msgb_l2(msg) ;
|
||||
struct abis_rsl_common_hdr *rslh;
|
||||
int rc = 0;
|
||||
|
||||
if (!msg) {
|
||||
DEBUGP(DRSL, "Empty RSL msg?..\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msgb_l2len(msg) < sizeof(*rslh)) {
|
||||
DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rslh = msgb_l2(msg);
|
||||
|
||||
switch (rslh->msg_discr & 0xfe) {
|
||||
case ABIS_RSL_MDISC_RLL:
|
||||
rc = abis_rsl_rx_rll(msg);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This software is based on ideas (but not code) of BS11Config
|
||||
* This software is based on ideas (but not code) of BS11Config
|
||||
* (C) 2009 by Dieter Spaar <spaar@mirider.augusta.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -54,9 +54,9 @@ static enum bs11cfg_state bs11cfg_state = STATE_NONE;
|
||||
static char *command, *value;
|
||||
struct timer_list status_timer;
|
||||
|
||||
static const u_int8_t obj_li_attr[] = {
|
||||
static const u_int8_t obj_li_attr[] = {
|
||||
NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00,
|
||||
NM_ATT_BS11_L1_PROT_TYPE, 0x00,
|
||||
NM_ATT_BS11_L1_PROT_TYPE, 0x00,
|
||||
NM_ATT_BS11_LINE_CFG, 0x00,
|
||||
};
|
||||
static const u_int8_t obj_bbsig0_attr[] = {
|
||||
@@ -71,7 +71,7 @@ static const char *trx1_password = "1111111111";
|
||||
|
||||
static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 };
|
||||
|
||||
static struct debug_target *stderr_target;
|
||||
static struct log_target *stderr_target;
|
||||
|
||||
/* dummy function to keep gsm_data.c happy */
|
||||
struct counter *counter_alloc(const char *name)
|
||||
@@ -778,7 +778,7 @@ static void handle_options(int argc, char **argv)
|
||||
serial_port = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
debug_parse_category_mask(stderr_target, optarg);
|
||||
log_parse_category_mask(stderr_target, optarg);
|
||||
break;
|
||||
case 's':
|
||||
fname_software = optarg;
|
||||
@@ -834,10 +834,10 @@ int main(int argc, char **argv)
|
||||
struct gsm_network *gsmnet;
|
||||
int rc;
|
||||
|
||||
debug_init();
|
||||
stderr_target = debug_target_create_stderr();
|
||||
debug_add_target(stderr_target);
|
||||
debug_set_all_filter(stderr_target, 1);
|
||||
log_init(&log_info);
|
||||
stderr_target = log_target_create_stderr();
|
||||
log_add_target(stderr_target);
|
||||
log_set_all_filter(stderr_target, 1);
|
||||
handle_options(argc, argv);
|
||||
bts_model_bs11_init();
|
||||
|
||||
@@ -870,3 +870,8 @@ int main(int argc, char **argv)
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* dummy to be able to compile */
|
||||
void gsm_net_update_ctype(struct gsm_network *net)
|
||||
{
|
||||
}
|
||||
|
||||
25
openbsc/src/bsc-nat.cfg
Normal file
25
openbsc/src/bsc-nat.cfg
Normal file
@@ -0,0 +1,25 @@
|
||||
!
|
||||
! BSC NAT configuration hand edited
|
||||
! !
|
||||
password foo
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
nat
|
||||
msc ip 10.0.0.23
|
||||
bsc 0
|
||||
token zecke
|
||||
location_area_code 3
|
||||
bsc 1
|
||||
token roch
|
||||
location_area_code 4
|
||||
mgcp
|
||||
local ip 10.0.0.23
|
||||
! bts ip 0.0.0.0
|
||||
bind ip 127.0.0.1
|
||||
bind port 2427
|
||||
bind early 1
|
||||
rtp base 4000
|
||||
number endpoints 31
|
||||
call agent ip 127.0.0.1
|
||||
33
openbsc/src/bsc_api.c
Normal file
33
openbsc/src/bsc_api.c
Normal file
@@ -0,0 +1,33 @@
|
||||
/* GSM 08.08 like API for OpenBSC. The bridge from MSC to BSC */
|
||||
|
||||
/* (C) 2010 by Holger Hans Peter Freyther
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openbsc/bsc_api.h>
|
||||
#include <openbsc/abis_rsl.h>
|
||||
|
||||
|
||||
int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn,
|
||||
struct msgb *msg, int link_id)
|
||||
{
|
||||
msg->lchan = conn->lchan;
|
||||
msg->trx = msg->lchan->ts->trx;
|
||||
return rsl_data_request(msg, link_id);
|
||||
}
|
||||
@@ -38,11 +38,12 @@
|
||||
#include <openbsc/signal.h>
|
||||
|
||||
/* MCC and MNC for the Location Area Identifier */
|
||||
static struct debug_target *stderr_target;
|
||||
static struct log_target *stderr_target;
|
||||
struct gsm_network *bsc_gsmnet = 0;
|
||||
static const char *database_name = "hlr.sqlite3";
|
||||
static const char *config_file = "openbsc.cfg";
|
||||
|
||||
extern const char *openbsc_version;
|
||||
extern const char *openbsc_copyright;
|
||||
|
||||
/* timer to store statistics */
|
||||
#define DB_SYNC_INTERVAL 60, 0
|
||||
@@ -75,12 +76,25 @@ static void print_help()
|
||||
printf(" Some useful help...\n");
|
||||
printf(" -h --help this text\n");
|
||||
printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n");
|
||||
printf(" -s --disable-color\n");
|
||||
printf(" -c --config-file filename The config file to use.\n");
|
||||
printf(" -s --disable-color\n");
|
||||
printf(" -l --database db-name The database to use\n");
|
||||
printf(" -a --authorize-everyone. Authorize every new subscriber. Dangerous!.\n");
|
||||
printf(" -p --pcap file The filename of the pcap file\n");
|
||||
printf(" -T --timestamp Prefix every log line with a timestamp\n");
|
||||
printf(" -V --version. Print the version of OpenBSC.\n");
|
||||
printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC\n");
|
||||
printf(" -e --log-level number. Set a global loglevel.\n");
|
||||
}
|
||||
|
||||
static void print_version()
|
||||
{
|
||||
printf("%s\n", openbsc_version);
|
||||
}
|
||||
|
||||
static void print_copyright()
|
||||
{
|
||||
puts(openbsc_copyright);
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char** argv)
|
||||
@@ -96,11 +110,13 @@ static void handle_options(int argc, char** argv)
|
||||
{"authorize-everyone", 0, 0, 'a'},
|
||||
{"pcap", 1, 0, 'p'},
|
||||
{"timestamp", 0, 0, 'T'},
|
||||
{"version", 0, 0, 'V' },
|
||||
{"rtp-proxy", 0, 0, 'P'},
|
||||
{"log-level", 1, 0, 'e'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hd:sl:ar:p:TPc:",
|
||||
c = getopt_long(argc, argv, "hd:sl:ar:p:TPVc:e:",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
@@ -111,10 +127,10 @@ static void handle_options(int argc, char** argv)
|
||||
print_help();
|
||||
exit(0);
|
||||
case 's':
|
||||
debug_set_use_color(stderr_target, 0);
|
||||
log_set_use_color(stderr_target, 0);
|
||||
break;
|
||||
case 'd':
|
||||
debug_parse_category_mask(stderr_target, optarg);
|
||||
log_parse_category_mask(stderr_target, optarg);
|
||||
break;
|
||||
case 'l':
|
||||
database_name = strdup(optarg);
|
||||
@@ -126,11 +142,20 @@ static void handle_options(int argc, char** argv)
|
||||
create_pcap_file(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
debug_set_print_timestamp(stderr_target, 1);
|
||||
log_set_print_timestamp(stderr_target, 1);
|
||||
break;
|
||||
case 'P':
|
||||
ipacc_rtp_direct = 0;
|
||||
break;
|
||||
case 'e':
|
||||
log_set_log_level(stderr_target, atoi(optarg));
|
||||
break;
|
||||
case 'V':
|
||||
print_version();
|
||||
printf("\n");
|
||||
print_copyright();
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
/* ignore */
|
||||
break;
|
||||
@@ -186,21 +211,21 @@ int main(int argc, char **argv)
|
||||
{
|
||||
int rc;
|
||||
|
||||
debug_init();
|
||||
log_init(&log_info);
|
||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
|
||||
talloc_ctx_init();
|
||||
on_dso_load_token();
|
||||
on_dso_load_rrlp();
|
||||
on_dso_load_ho_dec();
|
||||
stderr_target = debug_target_create_stderr();
|
||||
debug_add_target(stderr_target);
|
||||
stderr_target = log_target_create_stderr();
|
||||
log_add_target(stderr_target);
|
||||
|
||||
bts_model_unknown_init();
|
||||
bts_model_bs11_init();
|
||||
bts_model_nanobts_init();
|
||||
|
||||
/* enable filters */
|
||||
debug_set_all_filter(stderr_target, 1);
|
||||
log_set_all_filter(stderr_target, 1);
|
||||
|
||||
/* parse options */
|
||||
handle_options(argc, argv);
|
||||
@@ -237,7 +262,7 @@ int main(int argc, char **argv)
|
||||
|
||||
while (1) {
|
||||
bsc_upqueue(bsc_gsmnet);
|
||||
debug_reset_context();
|
||||
log_reset_context();
|
||||
bsc_select_main(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <openbsc/system_information.h>
|
||||
#include <openbsc/paging.h>
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/chan_alloc.h>
|
||||
#include <osmocore/talloc.h>
|
||||
|
||||
/* global pointer to the gsm network data structure */
|
||||
@@ -377,11 +378,11 @@ static unsigned char nanobts_attr_cell[] = {
|
||||
4, /* N3103 */
|
||||
8, /* N3105 */
|
||||
15, /* RLC CV countdown */
|
||||
NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00,
|
||||
NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00, /* CS1..CS4 */
|
||||
NM_ATT_IPACC_RLC_CFG_2, 0, 5,
|
||||
0x00, 250,
|
||||
0x00, 250,
|
||||
2, /* MCS2 */
|
||||
0x00, 250, /* T downlink TBF extension (0..500) */
|
||||
0x00, 250, /* T uplink TBF extension (0..500) */
|
||||
2, /* CS2 */
|
||||
#if 0
|
||||
/* EDGE model only, breaks older models.
|
||||
* Should inquire the BTS capabilities */
|
||||
@@ -417,9 +418,9 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
||||
switch (obj_class) {
|
||||
case NM_OC_SITE_MANAGER:
|
||||
bts = container_of(obj, struct gsm_bts, site_mgr);
|
||||
if ((new_state->operational == 2 &&
|
||||
if ((new_state->operational == NM_OPSTATE_ENABLED &&
|
||||
new_state->availability == NM_AVSTATE_OK) ||
|
||||
(new_state->operational == 1 &&
|
||||
(new_state->operational == NM_OPSTATE_DISABLED &&
|
||||
new_state->availability == NM_AVSTATE_OFF_LINE))
|
||||
abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff);
|
||||
break;
|
||||
@@ -439,7 +440,7 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
||||
case NM_OC_CHANNEL:
|
||||
ts = obj;
|
||||
trx = ts->trx;
|
||||
if (new_state->operational == 1 &&
|
||||
if (new_state->operational == NM_OPSTATE_DISABLED &&
|
||||
new_state->availability == NM_AVSTATE_DEPENDENCY) {
|
||||
patch_nm_tables(trx->bts);
|
||||
enum abis_nm_chan_comb ccomb =
|
||||
@@ -454,16 +455,16 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
||||
break;
|
||||
case NM_OC_RADIO_CARRIER:
|
||||
trx = obj;
|
||||
if (new_state->operational == 1 &&
|
||||
if (new_state->operational == NM_OPSTATE_DISABLED &&
|
||||
new_state->availability == NM_AVSTATE_OK)
|
||||
abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr,
|
||||
trx->nr, 0xff);
|
||||
break;
|
||||
case NM_OC_GPRS_NSE:
|
||||
bts = container_of(obj, struct gsm_bts, gprs.nse);
|
||||
if (!bts->gprs.enabled)
|
||||
if (bts->gprs.mode == BTS_GPRS_NONE)
|
||||
break;
|
||||
if (new_state->availability == 5) {
|
||||
if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
|
||||
abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
|
||||
0xff, 0xff, nanobts_attr_nse,
|
||||
sizeof(nanobts_attr_nse));
|
||||
@@ -475,9 +476,9 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
||||
break;
|
||||
case NM_OC_GPRS_CELL:
|
||||
bts = container_of(obj, struct gsm_bts, gprs.cell);
|
||||
if (!bts->gprs.enabled)
|
||||
if (bts->gprs.mode == BTS_GPRS_NONE)
|
||||
break;
|
||||
if (new_state->availability == 5) {
|
||||
if (new_state->availability == NM_AVSTATE_DEPENDENCY) {
|
||||
abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr,
|
||||
0, 0xff, nanobts_attr_cell,
|
||||
sizeof(nanobts_attr_cell));
|
||||
@@ -490,7 +491,7 @@ int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
||||
case NM_OC_GPRS_NSVC:
|
||||
nsvc = obj;
|
||||
bts = nsvc->bts;
|
||||
if (!bts->gprs.enabled)
|
||||
if (bts->gprs.mode == BTS_GPRS_NONE)
|
||||
break;
|
||||
/* We skip NSVC1 since we only use NSVC0 */
|
||||
if (nsvc->id == 1)
|
||||
@@ -798,7 +799,7 @@ static int set_system_infos(struct gsm_bts_trx *trx)
|
||||
DEBUGP(DRR, "SI%2u: %s\n", i, hexdump(si_tmp, rc));
|
||||
rsl_bcch_info(trx, i, si_tmp, sizeof(si_tmp));
|
||||
}
|
||||
if (bts->gprs.enabled) {
|
||||
if (bts->gprs.mode != BTS_GPRS_NONE) {
|
||||
i = 13;
|
||||
rc = gsm_generate_si(si_tmp, trx->bts, RSL_SYSTEM_INFO_13);
|
||||
if (rc < 0)
|
||||
@@ -852,6 +853,22 @@ static void patch_nm_tables(struct gsm_bts *bts)
|
||||
bs11_attr_radio[2] |= arfcn_high;
|
||||
bs11_attr_radio[3] = arfcn_low;
|
||||
|
||||
/* patch the RACH attributes */
|
||||
if (bts->rach_b_thresh != -1) {
|
||||
nanobts_attr_bts[33] = bts->rach_b_thresh & 0xff;
|
||||
bs11_attr_bts[33] = bts->rach_b_thresh & 0xff;
|
||||
}
|
||||
|
||||
if (bts->rach_ldavg_slots != -1) {
|
||||
u_int8_t avg_high = bts->rach_ldavg_slots & 0xff;
|
||||
u_int8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f;
|
||||
|
||||
nanobts_attr_bts[35] = avg_high;
|
||||
nanobts_attr_bts[36] = avg_low;
|
||||
bs11_attr_bts[35] = avg_high;
|
||||
bs11_attr_bts[36] = avg_low;
|
||||
}
|
||||
|
||||
/* patch BSIC */
|
||||
bs11_attr_bts[1] = bts->bsic;
|
||||
nanobts_attr_bts[sizeof(nanobts_attr_bts)-11] = bts->bsic;
|
||||
@@ -885,6 +902,11 @@ static void patch_nm_tables(struct gsm_bts *bts)
|
||||
/* patch RAC */
|
||||
nanobts_attr_cell[3] = bts->gprs.rac;
|
||||
|
||||
if (bts->gprs.mode == BTS_GPRS_EGPRS) {
|
||||
/* patch EGPRS coding schemes MCS 1..9 */
|
||||
nanobts_attr_cell[29] = 0x8f;
|
||||
nanobts_attr_cell[30] = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static void bootstrap_rsl(struct gsm_bts_trx *trx)
|
||||
@@ -899,6 +921,8 @@ static void bootstrap_rsl(struct gsm_bts_trx *trx)
|
||||
|
||||
void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
|
||||
{
|
||||
int ts_no, lchan_no;
|
||||
|
||||
switch (event) {
|
||||
case EVT_E1_TEI_UP:
|
||||
switch (type) {
|
||||
@@ -913,8 +937,35 @@ void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
|
||||
}
|
||||
break;
|
||||
case EVT_E1_TEI_DN:
|
||||
LOGP(DMI, LOGL_NOTICE, "Lost some E1 TEI link\n");
|
||||
/* FIXME: deal with TEI or L1 link loss */
|
||||
LOGP(DMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", type, trx);
|
||||
|
||||
if (type == E1INP_SIGN_OML)
|
||||
counter_inc(trx->bts->network->stats.bts.oml_fail);
|
||||
else if (type == E1INP_SIGN_RSL)
|
||||
counter_inc(trx->bts->network->stats.bts.rsl_fail);
|
||||
|
||||
/*
|
||||
* free all allocated channels. change the nm_state so the
|
||||
* trx and trx_ts becomes unusable and chan_alloc.c can not
|
||||
* allocate from it.
|
||||
*/
|
||||
for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) {
|
||||
struct gsm_bts_trx_ts *ts = &trx->ts[ts_no];
|
||||
|
||||
for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) {
|
||||
if (ts->lchan[lchan_no].state != GSM_LCHAN_NONE)
|
||||
lchan_free(&ts->lchan[lchan_no]);
|
||||
lchan_reset(&ts->lchan[lchan_no]);
|
||||
}
|
||||
|
||||
ts->nm_state.operational = 0;
|
||||
ts->nm_state.availability = 0;
|
||||
}
|
||||
|
||||
trx->nm_state.operational = 0;
|
||||
trx->nm_state.availability = 0;
|
||||
trx->bb_transc.nm_state.operational = 0;
|
||||
trx->bb_transc.nm_state.availability = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
241
openbsc/src/bsc_msc.c
Normal file
241
openbsc/src/bsc_msc.c
Normal file
@@ -0,0 +1,241 @@
|
||||
/* Routines to talk to the MSC using the IPA Protocol */
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openbsc/bsc_msc.h>
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
#include <osmocore/write_queue.h>
|
||||
#include <osmocore/talloc.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void connection_loss(struct bsc_msc_connection *con)
|
||||
{
|
||||
struct bsc_fd *fd;
|
||||
|
||||
fd = &con->write_queue.bfd;
|
||||
|
||||
close(fd->fd);
|
||||
fd->fd = -1;
|
||||
fd->cb = write_queue_bfd_cb;
|
||||
fd->when = 0;
|
||||
|
||||
con->is_connected = 0;
|
||||
con->connection_loss(con);
|
||||
}
|
||||
|
||||
static void msc_con_timeout(void *_con)
|
||||
{
|
||||
struct bsc_msc_connection *con = _con;
|
||||
|
||||
LOGP(DMSC, LOGL_ERROR, "MSC Connection timeout.\n");
|
||||
bsc_msc_lost(con);
|
||||
}
|
||||
|
||||
static int bsc_msc_except(struct bsc_fd *bfd)
|
||||
{
|
||||
struct write_queue *wrt;
|
||||
struct bsc_msc_connection *con;
|
||||
|
||||
LOGP(DMSC, LOGL_ERROR, "Exception on the BFD. Closing down.\n");
|
||||
|
||||
wrt = container_of(bfd, struct write_queue, bfd);
|
||||
con = container_of(wrt, struct bsc_msc_connection, write_queue);
|
||||
|
||||
connection_loss(con);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* called in the case of a non blocking connect */
|
||||
static int msc_connection_connect(struct bsc_fd *fd, unsigned int what)
|
||||
{
|
||||
int rc;
|
||||
int val;
|
||||
struct bsc_msc_connection *con;
|
||||
struct write_queue *queue;
|
||||
|
||||
socklen_t len = sizeof(val);
|
||||
|
||||
if ((what & BSC_FD_WRITE) == 0) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Callback but not readable.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
queue = container_of(fd, struct write_queue, bfd);
|
||||
con = container_of(queue, struct bsc_msc_connection, write_queue);
|
||||
|
||||
/* check the socket state */
|
||||
rc = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &val, &len);
|
||||
if (rc != 0) {
|
||||
LOGP(DMSC, LOGL_ERROR, "getsockopt for the MSC socket failed.\n");
|
||||
goto error;
|
||||
}
|
||||
if (val != 0) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Not connected to the MSC: %d\n", val);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
/* go to full operation */
|
||||
fd->cb = write_queue_bfd_cb;
|
||||
fd->when = BSC_FD_READ | BSC_FD_EXCEPT;
|
||||
|
||||
con->is_connected = 1;
|
||||
bsc_del_timer(&con->timeout_timer);
|
||||
LOGP(DMSC, LOGL_NOTICE, "(Re)Connected to the MSC.\n");
|
||||
if (con->connected)
|
||||
con->connected(con);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
bsc_unregister_fd(fd);
|
||||
connection_loss(con);
|
||||
return -1;
|
||||
}
|
||||
static void setnonblocking(struct bsc_fd *fd)
|
||||
{
|
||||
int flags;
|
||||
|
||||
flags = fcntl(fd->fd, F_GETFL);
|
||||
if (flags < 0) {
|
||||
perror("fcntl get failed");
|
||||
close(fd->fd);
|
||||
fd->fd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
flags |= O_NONBLOCK;
|
||||
flags = fcntl(fd->fd, F_SETFL, flags);
|
||||
if (flags < 0) {
|
||||
perror("fcntl get failed");
|
||||
close(fd->fd);
|
||||
fd->fd = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int bsc_msc_connect(struct bsc_msc_connection *con)
|
||||
{
|
||||
struct bsc_fd *fd;
|
||||
struct sockaddr_in sin;
|
||||
int on = 1, ret;
|
||||
|
||||
LOGP(DMSC, LOGL_NOTICE, "Attempting to connect MSC at %s:%d\n", con->ip, con->port);
|
||||
|
||||
con->is_connected = 0;
|
||||
|
||||
fd = &con->write_queue.bfd;
|
||||
fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
fd->data = NULL;
|
||||
fd->priv_nr = 1;
|
||||
|
||||
if (fd->fd < 0) {
|
||||
perror("Creating TCP socket failed");
|
||||
return fd->fd;
|
||||
}
|
||||
|
||||
/* make it non blocking */
|
||||
setnonblocking(fd);
|
||||
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons(con->port);
|
||||
inet_aton(con->ip, &sin.sin_addr);
|
||||
|
||||
setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin));
|
||||
|
||||
if (ret == -1 && errno == EINPROGRESS) {
|
||||
LOGP(DMSC, LOGL_ERROR, "MSC Connection in progress\n");
|
||||
fd->when = BSC_FD_WRITE;
|
||||
fd->cb = msc_connection_connect;
|
||||
con->timeout_timer.cb = msc_con_timeout;
|
||||
con->timeout_timer.data = con;
|
||||
bsc_schedule_timer(&con->timeout_timer, 20, 0);
|
||||
} else if (ret < 0) {
|
||||
perror("Connection failed");
|
||||
connection_loss(con);
|
||||
return ret;
|
||||
} else {
|
||||
fd->when = BSC_FD_READ | BSC_FD_EXCEPT;
|
||||
fd->cb = write_queue_bfd_cb;
|
||||
con->is_connected = 1;
|
||||
if (con->connected)
|
||||
con->connected(con);
|
||||
}
|
||||
|
||||
ret = bsc_register_fd(fd);
|
||||
if (ret < 0) {
|
||||
perror("Registering the fd failed");
|
||||
close(fd->fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
struct bsc_msc_connection *bsc_msc_create(const char *ip, int port)
|
||||
{
|
||||
struct bsc_msc_connection *con;
|
||||
|
||||
con = talloc_zero(NULL, struct bsc_msc_connection);
|
||||
if (!con) {
|
||||
LOGP(DMSC, LOGL_FATAL, "Failed to create the MSC connection.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
con->ip = ip;
|
||||
con->port = port;
|
||||
write_queue_init(&con->write_queue, 100);
|
||||
con->write_queue.except_cb = bsc_msc_except;
|
||||
return con;
|
||||
}
|
||||
|
||||
void bsc_msc_lost(struct bsc_msc_connection *con)
|
||||
{
|
||||
write_queue_clear(&con->write_queue);
|
||||
bsc_unregister_fd(&con->write_queue.bfd);
|
||||
connection_loss(con);
|
||||
}
|
||||
|
||||
static void reconnect_msc(void *_msc)
|
||||
{
|
||||
struct bsc_msc_connection *con = _msc;
|
||||
|
||||
LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n");
|
||||
bsc_msc_connect(con);
|
||||
}
|
||||
|
||||
void bsc_msc_schedule_connect(struct bsc_msc_connection *con)
|
||||
{
|
||||
LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n");
|
||||
con->reconnect_timer.cb = reconnect_msc;
|
||||
con->reconnect_timer.data = con;
|
||||
bsc_schedule_timer(&con->reconnect_timer, 5, 0);
|
||||
}
|
||||
1266
openbsc/src/bsc_msc_ip.c
Normal file
1266
openbsc/src/bsc_msc_ip.c
Normal file
File diff suppressed because it is too large
Load Diff
249
openbsc/src/bsc_msc_rf.c
Normal file
249
openbsc/src/bsc_msc_rf.c
Normal file
@@ -0,0 +1,249 @@
|
||||
/* RF Ctl handling socket */
|
||||
|
||||
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openbsc/bsc_msc_rf.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
|
||||
#include <osmocore/talloc.h>
|
||||
#include <osmocore/protocol/gsm_12_21.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define RF_CMD_QUERY '?'
|
||||
#define RF_CMD_OFF '0'
|
||||
#define RF_CMD_ON '1'
|
||||
|
||||
static int lock_each_trx(struct gsm_network *net, int lock)
|
||||
{
|
||||
struct gsm_bts *bts;
|
||||
|
||||
llist_for_each_entry(bts, &net->bts_list, list) {
|
||||
struct gsm_bts_trx *trx;
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
gsm_trx_lock_rf(trx, lock);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a '1' when one TRX is online, otherwise send 0
|
||||
*/
|
||||
static void handle_query(struct bsc_msc_rf_conn *conn)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct gsm_bts *bts;
|
||||
char send = '0';
|
||||
|
||||
llist_for_each_entry(bts, &conn->gsm_network->bts_list, list) {
|
||||
struct gsm_bts_trx *trx;
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
if (trx->nm_state.availability == NM_AVSTATE_OK &&
|
||||
trx->nm_state.operational != NM_STATE_LOCKED) {
|
||||
send = '1';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msg = msgb_alloc(10, "RF Query");
|
||||
if (!msg) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to allocate response msg.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msg->l2h = msgb_put(msg, 1);
|
||||
msg->l2h[0] = send;
|
||||
|
||||
if (write_queue_enqueue(&conn->queue, msg) != 0) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to enqueue the answer.\n");
|
||||
msgb_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int rf_read_cmd(struct bsc_fd *fd)
|
||||
{
|
||||
struct bsc_msc_rf_conn *conn = fd->data;
|
||||
char buf[1];
|
||||
int rc;
|
||||
|
||||
rc = read(fd->fd, buf, sizeof(buf));
|
||||
if (rc != sizeof(buf)) {
|
||||
LOGP(DINP, LOGL_ERROR, "Short read %d/%s\n", errno, strerror(errno));
|
||||
bsc_unregister_fd(fd);
|
||||
close(fd->fd);
|
||||
write_queue_clear(&conn->queue);
|
||||
talloc_free(conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (buf[0]) {
|
||||
case RF_CMD_QUERY:
|
||||
handle_query(conn);
|
||||
break;
|
||||
case RF_CMD_OFF:
|
||||
lock_each_trx(conn->gsm_network, 1);
|
||||
break;
|
||||
case RF_CMD_ON:
|
||||
lock_each_trx(conn->gsm_network, 0);
|
||||
break;
|
||||
default:
|
||||
LOGP(DINP, LOGL_ERROR, "Unknown command %d\n", buf[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rf_write_cmd(struct bsc_fd *fd, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = write(fd->fd, msg->data, msg->len);
|
||||
if (rc != msg->len) {
|
||||
LOGP(DINP, LOGL_ERROR, "Short write %d/%s\n", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rf_ctl_accept(struct bsc_fd *bfd, unsigned int what)
|
||||
{
|
||||
struct bsc_msc_rf_conn *conn;
|
||||
struct bsc_msc_rf *rf = bfd->data;
|
||||
struct sockaddr_un addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
int fd;
|
||||
|
||||
fd = accept(bfd->fd, (struct sockaddr *) &addr, &len);
|
||||
if (fd < 0) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to accept. errno: %d/%s\n",
|
||||
errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn = talloc_zero(rf, struct bsc_msc_rf_conn);
|
||||
if (!conn) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to allocate mem.\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_queue_init(&conn->queue, 10);
|
||||
conn->queue.bfd.data = conn;
|
||||
conn->queue.bfd.fd = fd;
|
||||
conn->queue.bfd.when = BSC_FD_READ | BSC_FD_WRITE;
|
||||
conn->queue.read_cb = rf_read_cmd;
|
||||
conn->queue.write_cb = rf_write_cmd;
|
||||
conn->gsm_network = rf->gsm_network;
|
||||
|
||||
if (bsc_register_fd(&conn->queue.bfd) != 0) {
|
||||
close(fd);
|
||||
talloc_free(conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bsc_msc_rf *bsc_msc_rf_create(const char *path, struct gsm_network *net)
|
||||
{
|
||||
unsigned int namelen;
|
||||
struct sockaddr_un local;
|
||||
struct bsc_fd *bfd;
|
||||
struct bsc_msc_rf *rf;
|
||||
int rc;
|
||||
|
||||
rf = talloc_zero(NULL, struct bsc_msc_rf);
|
||||
if (!rf) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to create bsc_msc_rf.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bfd = &rf->listen;
|
||||
bfd->fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (bfd->fd < 0) {
|
||||
LOGP(DINP, LOGL_ERROR, "Can not create socket. %d/%s\n",
|
||||
errno, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
local.sun_family = AF_UNIX;
|
||||
strncpy(local.sun_path, path, sizeof(local.sun_path));
|
||||
local.sun_path[sizeof(local.sun_path) - 1] = '\0';
|
||||
unlink(local.sun_path);
|
||||
|
||||
/* we use the same magic that X11 uses in Xtranssock.c for
|
||||
* calculating the proper length of the sockaddr */
|
||||
#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
|
||||
local.sun_len = strlen(local.sun_path);
|
||||
#endif
|
||||
#if defined(BSD44SOCKETS) || defined(SUN_LEN)
|
||||
namelen = SUN_LEN(&local);
|
||||
#else
|
||||
namelen = strlen(local.sun_path) +
|
||||
offsetof(struct sockaddr_un, sun_path);
|
||||
#endif
|
||||
|
||||
rc = bind(bfd->fd, (struct sockaddr *) &local, namelen);
|
||||
if (rc != 0) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to bind '%s' errno: %d/%s\n",
|
||||
local.sun_path, errno, strerror(errno));
|
||||
close(bfd->fd);
|
||||
talloc_free(rf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (listen(bfd->fd, 0) != 0) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to listen: %d/%s\n", errno, strerror(errno));
|
||||
close(bfd->fd);
|
||||
talloc_free(rf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bfd->when = BSC_FD_READ;
|
||||
bfd->cb = rf_ctl_accept;
|
||||
bfd->data = rf;
|
||||
|
||||
if (bsc_register_fd(bfd) != 0) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to register bfd.\n");
|
||||
close(bfd->fd);
|
||||
talloc_free(rf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rf->gsm_network = net;
|
||||
|
||||
return rf;
|
||||
}
|
||||
|
||||
@@ -51,8 +51,11 @@ static LLIST_HEAD(bsc_rll_reqs);
|
||||
|
||||
static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type)
|
||||
{
|
||||
struct gsm_subscriber_connection *conn;
|
||||
|
||||
conn = &rllr->lchan->conn;
|
||||
llist_del(&rllr->list);
|
||||
put_lchan(rllr->lchan);
|
||||
put_subscr_con(conn, 0);
|
||||
rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type);
|
||||
talloc_free(rllr);
|
||||
}
|
||||
@@ -70,6 +73,7 @@ int rll_establish(struct gsm_lchan *lchan, u_int8_t sapi,
|
||||
enum bsc_rllr_ind),
|
||||
void *data)
|
||||
{
|
||||
struct gsm_subscriber_connection *conn;
|
||||
struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req);
|
||||
u_int8_t link_id;
|
||||
if (!rllr)
|
||||
@@ -80,11 +84,11 @@ int rll_establish(struct gsm_lchan *lchan, u_int8_t sapi,
|
||||
/* If we are a TCH and not in signalling mode, we need to
|
||||
* indicate that the new RLL connection is to be made on the SACCH */
|
||||
if ((lchan->type == GSM_LCHAN_TCH_F ||
|
||||
lchan->type == GSM_LCHAN_TCH_H) &&
|
||||
lchan->rsl_cmode != RSL_CMOD_SPD_SIGN)
|
||||
lchan->type == GSM_LCHAN_TCH_H) && sapi != 0)
|
||||
link_id |= 0x40;
|
||||
|
||||
use_lchan(lchan);
|
||||
conn = &lchan->conn;
|
||||
use_subscr_con(conn);
|
||||
rllr->lchan = lchan;
|
||||
rllr->link_id = link_id;
|
||||
rllr->cb = cb;
|
||||
|
||||
32
openbsc/src/bsc_version.c
Normal file
32
openbsc/src/bsc_version.c
Normal file
@@ -0,0 +1,32 @@
|
||||
/* Hold the copyright and version string */
|
||||
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "bscconfig.h"
|
||||
|
||||
const char *openbsc_version = "OpenBSC " PACKAGE_VERSION;
|
||||
const char *openbsc_copyright =
|
||||
"Copyright (C) 2008-2010 Harald Welte, Holger Freyther\n"
|
||||
"Contributions by Daniel Willmann, Jan Lübbe,Stefan Schmidt\n"
|
||||
"Dieter Spaar, Andreas Eversberg\n\n"
|
||||
"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
|
||||
"This is free software: you are free to change and redistribute it.\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\n";
|
||||
|
||||
|
||||
1341
openbsc/src/bssap.c
Normal file
1341
openbsc/src/bssap.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -33,8 +33,6 @@
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/signal.h>
|
||||
|
||||
static void auto_release_channel(void *_lchan);
|
||||
|
||||
static int ts_is_usable(struct gsm_bts_trx_ts *ts)
|
||||
{
|
||||
/* FIXME: How does this behave for BS-11 ? */
|
||||
@@ -152,6 +150,7 @@ static const u_int8_t subslots_per_pchan[] = {
|
||||
[GSM_PCHAN_TCH_H] = 2,
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
|
||||
/* FIXME: what about dynamic TCH_F_TCH_H ? */
|
||||
[GSM_PCHAN_TCH_F_PDCH] = 1,
|
||||
};
|
||||
|
||||
static struct gsm_lchan *
|
||||
@@ -167,7 +166,14 @@ _lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan)
|
||||
ts = &trx->ts[j];
|
||||
if (!ts_is_usable(ts))
|
||||
continue;
|
||||
if (ts->pchan != pchan)
|
||||
/* ip.access dynamic TCH/F + PDCH combination */
|
||||
if (ts->pchan == GSM_PCHAN_TCH_F_PDCH &&
|
||||
pchan == GSM_PCHAN_TCH_F) {
|
||||
/* we can only consider such a dynamic channel
|
||||
* if the PDCH is currently inactive */
|
||||
if (ts->flags & TS_F_PDCH_MODE)
|
||||
continue;
|
||||
} else if (ts->pchan != pchan)
|
||||
continue;
|
||||
/* check if all sub-slots are allocated yet */
|
||||
for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) {
|
||||
@@ -177,6 +183,7 @@ _lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan)
|
||||
return lc;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -216,7 +223,8 @@ _lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
|
||||
}
|
||||
|
||||
/* Allocate a logical channel */
|
||||
struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
|
||||
struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type,
|
||||
int allow_bigger)
|
||||
{
|
||||
struct gsm_lchan *lchan = NULL;
|
||||
enum gsm_phys_chan_config first, second;
|
||||
@@ -234,6 +242,19 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
|
||||
lchan = _lc_find_bts(bts, first);
|
||||
if (lchan == NULL)
|
||||
lchan = _lc_find_bts(bts, second);
|
||||
|
||||
/* allow to assign bigger channels */
|
||||
if (allow_bigger) {
|
||||
if (lchan == NULL) {
|
||||
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H);
|
||||
type = GSM_LCHAN_TCH_H;
|
||||
}
|
||||
|
||||
if (lchan == NULL) {
|
||||
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
|
||||
type = GSM_LCHAN_TCH_F;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GSM_LCHAN_TCH_F:
|
||||
lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F);
|
||||
@@ -252,7 +273,6 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
|
||||
|
||||
if (lchan) {
|
||||
lchan->type = type;
|
||||
lchan->use_count = 0;
|
||||
|
||||
/* clear sapis */
|
||||
memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis));
|
||||
@@ -260,10 +280,18 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
|
||||
/* clear multi rate config */
|
||||
memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf));
|
||||
|
||||
/* Configure the time and start it so it will be closed */
|
||||
lchan->release_timer.cb = auto_release_channel;
|
||||
lchan->release_timer.data = lchan;
|
||||
bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT);
|
||||
/* clear any msc reference */
|
||||
lchan->msc_data = NULL;
|
||||
|
||||
/* clear per MSC/BSC data */
|
||||
memset(&lchan->conn, 0, sizeof(lchan->conn));
|
||||
lchan->conn.lchan = lchan;
|
||||
lchan->conn.bts = lchan->ts->trx->bts;
|
||||
} else {
|
||||
struct challoc_signal_data sig;
|
||||
sig.bts = bts;
|
||||
sig.type = type;
|
||||
dispatch_signal(SS_CHALLOC, S_CHALLOC_ALLOC_FAIL, &sig);
|
||||
}
|
||||
|
||||
return lchan;
|
||||
@@ -272,22 +300,22 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type)
|
||||
/* Free a logical channel */
|
||||
void lchan_free(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct challoc_signal_data sig;
|
||||
int i;
|
||||
|
||||
sig.type = lchan->type;
|
||||
lchan->type = GSM_LCHAN_NONE;
|
||||
if (lchan->subscr) {
|
||||
subscr_put(lchan->subscr);
|
||||
lchan->subscr = NULL;
|
||||
if (lchan->conn.subscr) {
|
||||
subscr_put(lchan->conn.subscr);
|
||||
lchan->conn.subscr = NULL;
|
||||
}
|
||||
|
||||
/* We might kill an active channel... */
|
||||
if (lchan->use_count != 0) {
|
||||
if (lchan->conn.use_count != 0) {
|
||||
dispatch_signal(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, lchan);
|
||||
lchan->use_count = 0;
|
||||
lchan->conn.use_count = 0;
|
||||
}
|
||||
|
||||
/* stop the timer */
|
||||
bsc_del_timer(&lchan->release_timer);
|
||||
bsc_del_timer(&lchan->T3101);
|
||||
|
||||
/* clear cached measuement reports */
|
||||
@@ -298,54 +326,118 @@ void lchan_free(struct gsm_lchan *lchan)
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++)
|
||||
lchan->neigh_meas[i].arfcn = 0;
|
||||
lchan->conn.silent_call = 0;
|
||||
|
||||
lchan->silent_call = 0;
|
||||
sig.lchan = lchan;
|
||||
sig.bts = lchan->ts->trx->bts;
|
||||
dispatch_signal(SS_CHALLOC, S_CHALLOC_FREED, &sig);
|
||||
|
||||
/* FIXME: ts_free() the timeslot, if we're the last logical
|
||||
* channel using it */
|
||||
}
|
||||
|
||||
/* Consider releasing the channel now */
|
||||
int lchan_auto_release(struct gsm_lchan *lchan)
|
||||
/*
|
||||
* There was an error with the TRX and we need to forget
|
||||
* any state so that a lchan can be allocated again after
|
||||
* the trx is fully usable.
|
||||
*/
|
||||
void lchan_reset(struct gsm_lchan *lchan)
|
||||
{
|
||||
if (lchan->use_count > 0) {
|
||||
bsc_del_timer(&lchan->T3101);
|
||||
bsc_del_timer(&lchan->T3111);
|
||||
bsc_del_timer(&lchan->error_timer);
|
||||
|
||||
lchan->type = GSM_LCHAN_NONE;
|
||||
lchan->state = LCHAN_S_NONE;
|
||||
}
|
||||
|
||||
static int _lchan_release_next_sapi(struct gsm_lchan *lchan)
|
||||
{
|
||||
int sapi;
|
||||
|
||||
for (sapi = 1; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) {
|
||||
u_int8_t link_id;
|
||||
if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED)
|
||||
continue;
|
||||
|
||||
link_id = sapi;
|
||||
if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H)
|
||||
link_id |= 0x40;
|
||||
rsl_release_request(lchan, link_id, lchan->release_reason);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Assume we have GSM04.08 running and send a release */
|
||||
if (lchan->subscr) {
|
||||
gsm48_send_rr_release(lchan);
|
||||
}
|
||||
|
||||
/* spoofed? message */
|
||||
if (lchan->use_count < 0)
|
||||
LOGP(DRLL, LOGL_ERROR, "Channel count is negative: %d\n",
|
||||
lchan->use_count);
|
||||
|
||||
DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan));
|
||||
rsl_release_request(lchan, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Auto release the channel when the use count is zero */
|
||||
static void auto_release_channel(void *_lchan)
|
||||
static void _lchan_handle_release(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct gsm_lchan *lchan = _lchan;
|
||||
/* Ask for SAPI != 0 to be freed first and stop if we need to wait */
|
||||
if (_lchan_release_next_sapi(lchan) == 0)
|
||||
return;
|
||||
|
||||
if (!lchan_auto_release(lchan))
|
||||
bsc_schedule_timer(&lchan->release_timer, LCHAN_RELEASE_TIMEOUT);
|
||||
/* Assume we have GSM04.08 running and send a release */
|
||||
if (lchan->conn.subscr) {
|
||||
++lchan->conn.use_count;
|
||||
gsm48_send_rr_release(lchan);
|
||||
--lchan->conn.use_count;
|
||||
}
|
||||
|
||||
/* spoofed? message */
|
||||
if (lchan->conn.use_count < 0)
|
||||
LOGP(DRLL, LOGL_ERROR, "Channel count is negative: %d\n",
|
||||
lchan->conn.use_count);
|
||||
|
||||
rsl_release_request(lchan, 0, lchan->release_reason);
|
||||
rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ);
|
||||
}
|
||||
|
||||
/* called from abis rsl */
|
||||
int rsl_lchan_rll_release(struct gsm_lchan *lchan, u_int8_t link_id)
|
||||
{
|
||||
if (lchan->state != LCHAN_S_REL_REQ)
|
||||
return -1;
|
||||
|
||||
if ((link_id & 0x7) != 0)
|
||||
_lchan_handle_release(lchan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Start the channel release procedure now. We will start by shutting
|
||||
* down SAPI!=0, then we will deactivate the SACCH and finish by releasing
|
||||
* the last SAPI at which point the RSL code will send the channel release
|
||||
* for us. We should guard the whole shutdown by T3109 or similiar and then
|
||||
* update the fixme inside gsm_04_08_utils.c
|
||||
* When we request to release the RLL and we don't get an answer within T200
|
||||
* the BTS will send us an Error indication which we will handle by closing
|
||||
* the channel and be done.
|
||||
*/
|
||||
int _lchan_release(struct gsm_lchan *lchan, u_int8_t release_reason)
|
||||
{
|
||||
if (lchan->conn.use_count > 0) {
|
||||
DEBUGP(DRLL, "BUG: _lchan_release called without zero use_count.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan));
|
||||
rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ);
|
||||
lchan->release_reason = release_reason;
|
||||
_lchan_handle_release(lchan);
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) {
|
||||
struct gsm_bts_trx *trx;
|
||||
int ts_no, lchan_no;
|
||||
int ts_no, lchan_no;
|
||||
|
||||
llist_for_each_entry(trx, &bts->trx_list, list) {
|
||||
for (ts_no = 0; ts_no < 8; ++ts_no) {
|
||||
for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) {
|
||||
struct gsm_lchan *lchan =
|
||||
&trx->ts[ts_no].lchan[lchan_no];
|
||||
if (subscr == lchan->subscr)
|
||||
if (subscr == lchan->conn.subscr)
|
||||
return lchan;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, char *imsi)
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
/* Is this subscriber known in the db? */
|
||||
subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi);
|
||||
subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi);
|
||||
if (subscr) {
|
||||
result = dbi_conn_queryf(conn,
|
||||
"UPDATE Subscriber set updated = datetime('now') "
|
||||
@@ -288,6 +288,8 @@ struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, char *imsi)
|
||||
return subscr;
|
||||
}
|
||||
|
||||
static_assert(sizeof(unsigned char) == sizeof(struct gsm48_classmark1), classmark1_size);
|
||||
|
||||
static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
|
||||
{
|
||||
dbi_result result;
|
||||
@@ -316,9 +318,10 @@ static int get_equipment_by_subscr(struct gsm_subscriber *subscr)
|
||||
strncpy(equip->imei, string, sizeof(equip->imei));
|
||||
|
||||
string = dbi_result_get_string(result, "classmark1");
|
||||
if (string)
|
||||
cm1 = atoi(string) & 0xff;
|
||||
equip->classmark1 = *((struct gsm48_classmark1 *) &cm1);
|
||||
if (string) {
|
||||
cm1 = atoi(string) & 0xff;
|
||||
memcpy(&equip->classmark1, &cm1, sizeof(equip->classmark1));
|
||||
}
|
||||
|
||||
equip->classmark2_len = dbi_result_get_field_length(result, "classmark2");
|
||||
cm2 = dbi_result_get_binary(result, "classmark2");
|
||||
@@ -1014,7 +1017,7 @@ static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result resul
|
||||
}
|
||||
|
||||
/* retrieve the next unsent SMS with ID >= min_id */
|
||||
struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
|
||||
struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id)
|
||||
{
|
||||
dbi_result result;
|
||||
struct gsm_sms *sms;
|
||||
@@ -1041,7 +1044,7 @@ struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, int min_id)
|
||||
return sms;
|
||||
}
|
||||
|
||||
struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, int min_subscr_id)
|
||||
struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, unsigned long long min_subscr_id)
|
||||
{
|
||||
dbi_result result;
|
||||
struct gsm_sms *sms;
|
||||
@@ -1049,7 +1052,7 @@ struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, int min_sub
|
||||
result = dbi_conn_queryf(conn,
|
||||
"SELECT * FROM SMS,Subscriber "
|
||||
"WHERE sms.receiver_id >= %llu AND sms.sent is NULL "
|
||||
"AND sms.receiver_id = subscriber.id "
|
||||
"AND sms.receiver_id = subscriber.id "
|
||||
"AND subscriber.lac > 0 "
|
||||
"ORDER BY sms.receiver_id, id LIMIT 1",
|
||||
min_subscr_id);
|
||||
@@ -1133,7 +1136,7 @@ int db_sms_inc_deliver_attempts(struct gsm_sms *sms)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_apdu_blob_store(struct gsm_subscriber *subscr,
|
||||
int db_apdu_blob_store(struct gsm_subscriber *subscr,
|
||||
u_int8_t apdu_id_flags, u_int8_t len,
|
||||
u_int8_t *apdu)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* Debugging/Logging support code */
|
||||
/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
|
||||
/* OpenBSC Debugging/Logging support code */
|
||||
|
||||
/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
@@ -27,401 +28,166 @@
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <openbsc/debug.h>
|
||||
#include <osmocore/talloc.h>
|
||||
#include <osmocore/utils.h>
|
||||
#include <osmocore/logging.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
/* default categories */
|
||||
static struct debug_category default_categories[Debug_LastEntry] = {
|
||||
[DRLL] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DCC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DNM] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DRR] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DRSL] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DMM] = { .enabled = 1, .loglevel = LOGL_INFO },
|
||||
[DMNCC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DSMS] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DPAG] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DMEAS] = { .enabled = 0, .loglevel = LOGL_NOTICE },
|
||||
[DMI] = { .enabled = 0, .loglevel = LOGL_NOTICE },
|
||||
[DMIB] = { .enabled = 0, .loglevel = LOGL_NOTICE },
|
||||
[DMUX] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DINP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DSCCP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DMSC] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DMGCP] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DHO] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DDB] = { .enabled = 1, .loglevel = LOGL_NOTICE },
|
||||
[DREF] = { .enabled = 0, .loglevel = LOGL_NOTICE },
|
||||
static const struct log_info_cat default_categories[] = {
|
||||
[DRLL] = {
|
||||
.name = "DRLL",
|
||||
.description = "Radio Link Layer",
|
||||
.color = "\033[1;31m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DCC] = {
|
||||
.name = "DCC",
|
||||
.description = "Call Control",
|
||||
.color = "\033[1;32m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DMM] = {
|
||||
.name = "DMM",
|
||||
.description = "Mobility Management",
|
||||
.color = "\033[1;33m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DRR] = {
|
||||
.name = "DRR",
|
||||
.description = "Radio Resource",
|
||||
.color = "\033[1;34m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DRSL] = {
|
||||
.name = "DRSL",
|
||||
.description = "Radio Siganlling Link",
|
||||
.color = "\033[1;35m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DNM] = {
|
||||
.name = "DNM",
|
||||
.description = "Network Management (OML)",
|
||||
.color = "\033[1;36m",
|
||||
.enabled = 1, .loglevel = LOGL_INFO,
|
||||
},
|
||||
[DMNCC] = {
|
||||
.name = "DMNCC",
|
||||
.description = "BSC<->MSC interface",
|
||||
.color = "\033[1;39m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DSMS] = {
|
||||
.name = "DSMS",
|
||||
.description = "Short Message Service",
|
||||
.color = "\033[1;37m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DPAG] = {
|
||||
.name = "DPAG",
|
||||
.description = "Paging",
|
||||
.color = "\033[1;38m",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DMEAS] = {
|
||||
.name = "DMEAS",
|
||||
.description = "Measurement Processing",
|
||||
.enabled = 0, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DMI] = {
|
||||
.name = "DMI",
|
||||
.description = "mISDN Input Driver",
|
||||
.enabled = 0, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DMIB] = {
|
||||
.name = "DMIB",
|
||||
.description = "mISDN B-Channels",
|
||||
.enabled = 0, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DMUX] = {
|
||||
.name = "DMUX",
|
||||
.description = "TRAU Frame Multiplex",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DINP] = {
|
||||
.name = "DINP",
|
||||
.description = "Input Driver",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DSCCP] = {
|
||||
.name = "DSCCP",
|
||||
.description = "SCCP Protocol",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DMSC] = {
|
||||
.name = "DMSC",
|
||||
.description = "Mobile Switching Center",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DMGCP] = {
|
||||
.name = "DMGCP",
|
||||
.description = "Media Gateway Control Protocol",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DHO] = {
|
||||
.name = "DHO",
|
||||
.description = "Hand-Over",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DDB] = {
|
||||
.name = "DDB",
|
||||
.description = "Database",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DREF] = {
|
||||
.name = "DREF",
|
||||
.description = "Reference Counting",
|
||||
.enabled = 0, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
[DNAT] = {
|
||||
.name = "DNAT",
|
||||
.description = "BSC MUX/NAT",
|
||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
||||
},
|
||||
};
|
||||
|
||||
struct debug_info {
|
||||
const char *name;
|
||||
const char *color;
|
||||
const char *description;
|
||||
int number;
|
||||
int position;
|
||||
enum log_ctxt {
|
||||
CTX_SUBSCRIBER,
|
||||
};
|
||||
|
||||
struct debug_context {
|
||||
struct gsm_lchan *lchan;
|
||||
struct gsm_subscriber *subscr;
|
||||
struct gsm_bts *bts;
|
||||
enum log_filter {
|
||||
_FLT_ALL = LOG_FILTER_ALL, /* libosmocore */
|
||||
FLT_IMSI = 1,
|
||||
};
|
||||
|
||||
static struct debug_context debug_context;
|
||||
static void *tall_dbg_ctx = NULL;
|
||||
static LLIST_HEAD(target_list);
|
||||
static int filter_fn(const struct log_context *ctx,
|
||||
struct log_target *tar)
|
||||
{
|
||||
struct gsm_subscriber *subscr = ctx->ctx[CTX_SUBSCRIBER];
|
||||
|
||||
#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \
|
||||
{ .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER },
|
||||
if ((tar->filter_map & (1 << FLT_IMSI)) != 0
|
||||
&& subscr && strcmp(subscr->imsi, tar->filter_data[FLT_IMSI]) == 0)
|
||||
return 1;
|
||||
|
||||
static const struct debug_info debug_info[] = {
|
||||
DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "")
|
||||
DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "")
|
||||
DEBUG_CATEGORY(DMM, "DMM", "\033[1;33m", "")
|
||||
DEBUG_CATEGORY(DRR, "DRR", "\033[1;34m", "")
|
||||
DEBUG_CATEGORY(DRSL, "DRSL", "\033[1;35m", "")
|
||||
DEBUG_CATEGORY(DNM, "DNM", "\033[1;36m", "")
|
||||
DEBUG_CATEGORY(DSMS, "DSMS", "\033[1;37m", "")
|
||||
DEBUG_CATEGORY(DPAG, "DPAG", "\033[1;38m", "")
|
||||
DEBUG_CATEGORY(DMNCC, "DMNCC","\033[1;39m", "")
|
||||
DEBUG_CATEGORY(DINP, "DINP", "", "")
|
||||
DEBUG_CATEGORY(DMI, "DMI", "", "")
|
||||
DEBUG_CATEGORY(DMIB, "DMIB", "", "")
|
||||
DEBUG_CATEGORY(DMUX, "DMUX", "", "")
|
||||
DEBUG_CATEGORY(DMEAS, "DMEAS", "", "")
|
||||
DEBUG_CATEGORY(DSCCP, "DSCCP", "", "")
|
||||
DEBUG_CATEGORY(DMSC, "DMSC", "", "")
|
||||
DEBUG_CATEGORY(DMGCP, "DMGCP", "", "")
|
||||
DEBUG_CATEGORY(DHO, "DHO", "", "")
|
||||
DEBUG_CATEGORY(DDB, "DDB", "", "")
|
||||
DEBUG_CATEGORY(DREF, "DREF", "", "")
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct log_info log_info = {
|
||||
.filter_fn = filter_fn,
|
||||
.cat = default_categories,
|
||||
.num_cat = ARRAY_SIZE(default_categories),
|
||||
};
|
||||
|
||||
static const struct value_string loglevel_strs[] = {
|
||||
{ 0, "EVERYTHING" },
|
||||
{ 1, "DEBUG" },
|
||||
{ 3, "INFO" },
|
||||
{ 5, "NOTICE" },
|
||||
{ 7, "ERROR" },
|
||||
{ 8, "FATAL" },
|
||||
{ 0, NULL },
|
||||
};
|
||||
|
||||
int debug_parse_level(const char *lvl)
|
||||
{
|
||||
return get_string_value(loglevel_strs, lvl);
|
||||
}
|
||||
|
||||
int debug_parse_category(const char *category)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
|
||||
if (!strcasecmp(debug_info[i].name+1, category))
|
||||
return debug_info[i].number;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the category mask.
|
||||
* The format can be this: category1:category2:category3
|
||||
* or category1,2:category2,3:...
|
||||
*/
|
||||
void debug_parse_category_mask(struct debug_target* target, const char *_mask)
|
||||
{
|
||||
int i = 0;
|
||||
char *mask = strdup(_mask);
|
||||
char *category_token = NULL;
|
||||
|
||||
/* Disable everything to enable it afterwards */
|
||||
for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
|
||||
target->categories[i].enabled = 0;
|
||||
|
||||
category_token = strtok(mask, ":");
|
||||
do {
|
||||
for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
|
||||
char* colon = strstr(category_token, ",");
|
||||
int length = strlen(category_token);
|
||||
|
||||
if (colon)
|
||||
length = colon - category_token;
|
||||
|
||||
if (strncasecmp(debug_info[i].name, category_token, length) == 0) {
|
||||
int number = debug_info[i].number;
|
||||
int level = 0;
|
||||
|
||||
if (colon)
|
||||
level = atoi(colon+1);
|
||||
|
||||
target->categories[number].enabled = 1;
|
||||
target->categories[number].loglevel = level;
|
||||
}
|
||||
}
|
||||
} while ((category_token = strtok(NULL, ":")));
|
||||
|
||||
free(mask);
|
||||
}
|
||||
|
||||
static const char* color(int subsys)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
|
||||
if (debug_info[i].number == subsys)
|
||||
return debug_info[i].color;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static void _output(struct debug_target *target, unsigned int subsys, char *file, int line,
|
||||
int cont, const char *format, va_list ap)
|
||||
{
|
||||
char col[30];
|
||||
char sub[30];
|
||||
char tim[30];
|
||||
char buf[4096];
|
||||
char final[4096];
|
||||
|
||||
/* prepare the data */
|
||||
col[0] = '\0';
|
||||
sub[0] = '\0';
|
||||
tim[0] = '\0';
|
||||
buf[0] = '\0';
|
||||
|
||||
/* are we using color */
|
||||
if (target->use_color) {
|
||||
snprintf(col, sizeof(col), "%s", color(subsys));
|
||||
col[sizeof(col)-1] = '\0';
|
||||
}
|
||||
vsnprintf(buf, sizeof(buf), format, ap);
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
if (!cont) {
|
||||
if (target->print_timestamp) {
|
||||
char *timestr;
|
||||
time_t tm;
|
||||
tm = time(NULL);
|
||||
timestr = ctime(&tm);
|
||||
timestr[strlen(timestr)-1] = '\0';
|
||||
snprintf(tim, sizeof(tim), "%s ", timestr);
|
||||
tim[sizeof(tim)-1] = '\0';
|
||||
}
|
||||
snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
|
||||
sub[sizeof(sub)-1] = '\0';
|
||||
}
|
||||
|
||||
snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
|
||||
final[sizeof(final)-1] = '\0';
|
||||
target->output(target, final);
|
||||
}
|
||||
|
||||
|
||||
static void _debugp(unsigned int subsys, int level, char *file, int line,
|
||||
int cont, const char *format, va_list ap)
|
||||
{
|
||||
struct debug_target *tar;
|
||||
|
||||
llist_for_each_entry(tar, &target_list, entry) {
|
||||
struct debug_category *category;
|
||||
int output = 0;
|
||||
|
||||
category = &tar->categories[subsys];
|
||||
/* subsystem is not supposed to be debugged */
|
||||
if (!category->enabled)
|
||||
continue;
|
||||
|
||||
/* Check the global log level */
|
||||
if (tar->loglevel != 0 && level < tar->loglevel)
|
||||
continue;
|
||||
|
||||
/* Check the category log level */
|
||||
if (category->loglevel != 0 && level < category->loglevel)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Apply filters here... if that becomes messy we will need to put
|
||||
* filters in a list and each filter will say stop, continue, output
|
||||
*/
|
||||
if ((tar->filter_map & DEBUG_FILTER_ALL) != 0) {
|
||||
output = 1;
|
||||
} else if ((tar->filter_map & DEBUG_FILTER_IMSI) != 0
|
||||
&& debug_context.subscr && strcmp(debug_context.subscr->imsi, tar->imsi_filter) == 0) {
|
||||
output = 1;
|
||||
}
|
||||
|
||||
if (output) {
|
||||
/* FIXME: copying the va_list is an ugly workaround against a bug
|
||||
* hidden somewhere in _output. If we do not copy here, the first
|
||||
* call to _output() will corrupt the va_list contents, and any
|
||||
* further _output() calls with the same va_list will segfault */
|
||||
va_list bp;
|
||||
va_copy(bp, ap);
|
||||
_output(tar, subsys, file, line, cont, format, bp);
|
||||
va_end(bp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
_debugp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
_debugp(subsys, level, file, line, cont, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static char hexd_buff[4096];
|
||||
|
||||
char *hexdump(const unsigned char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
char *cur = hexd_buff;
|
||||
|
||||
hexd_buff[0] = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
|
||||
int rc = snprintf(cur, len_remain, "%02x ", buf[i]);
|
||||
if (rc <= 0)
|
||||
break;
|
||||
cur += rc;
|
||||
}
|
||||
hexd_buff[sizeof(hexd_buff)-1] = 0;
|
||||
return hexd_buff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void debug_add_target(struct debug_target *target)
|
||||
{
|
||||
llist_add_tail(&target->entry, &target_list);
|
||||
}
|
||||
|
||||
void debug_del_target(struct debug_target *target)
|
||||
{
|
||||
llist_del(&target->entry);
|
||||
}
|
||||
|
||||
void debug_reset_context(void)
|
||||
{
|
||||
memset(&debug_context, 0, sizeof(debug_context));
|
||||
}
|
||||
|
||||
/* currently we are not reffing these */
|
||||
void debug_set_context(int ctx, void *value)
|
||||
{
|
||||
switch (ctx) {
|
||||
case BSC_CTX_LCHAN:
|
||||
debug_context.lchan = (struct gsm_lchan *) value;
|
||||
break;
|
||||
case BSC_CTX_SUBSCR:
|
||||
debug_context.subscr = (struct gsm_subscriber *) value;
|
||||
break;
|
||||
case BSC_CTX_BTS:
|
||||
debug_context.bts = (struct gsm_bts *) value;
|
||||
break;
|
||||
case BSC_CTX_SCCP:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void debug_set_imsi_filter(struct debug_target *target, const char *imsi)
|
||||
void log_set_imsi_filter(struct log_target *target, const char *imsi)
|
||||
{
|
||||
if (imsi) {
|
||||
target->filter_map |= DEBUG_FILTER_IMSI;
|
||||
target->imsi_filter = talloc_strdup(target, imsi);
|
||||
} else if (target->imsi_filter) {
|
||||
target->filter_map &= ~DEBUG_FILTER_IMSI;
|
||||
talloc_free(target->imsi_filter);
|
||||
target->imsi_filter = NULL;
|
||||
target->filter_map |= (1 << FLT_IMSI);
|
||||
target->filter_data[FLT_IMSI] = talloc_strdup(target, imsi);
|
||||
} else if (target->filter_data[FLT_IMSI]) {
|
||||
target->filter_map &= ~(1 << FLT_IMSI);
|
||||
talloc_free(target->filter_data[FLT_IMSI]);
|
||||
target->filter_data[FLT_IMSI] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void debug_set_all_filter(struct debug_target *target, int all)
|
||||
{
|
||||
if (all)
|
||||
target->filter_map |= DEBUG_FILTER_ALL;
|
||||
else
|
||||
target->filter_map &= ~DEBUG_FILTER_ALL;
|
||||
}
|
||||
|
||||
void debug_set_use_color(struct debug_target *target, int use_color)
|
||||
{
|
||||
target->use_color = use_color;
|
||||
}
|
||||
|
||||
void debug_set_print_timestamp(struct debug_target *target, int print_timestamp)
|
||||
{
|
||||
target->print_timestamp = print_timestamp;
|
||||
}
|
||||
|
||||
void debug_set_log_level(struct debug_target *target, int log_level)
|
||||
{
|
||||
target->loglevel = log_level;
|
||||
}
|
||||
|
||||
void debug_set_category_filter(struct debug_target *target, int category, int enable, int level)
|
||||
{
|
||||
if (category >= Debug_LastEntry)
|
||||
return;
|
||||
target->categories[category].enabled = !!enable;
|
||||
target->categories[category].loglevel = level;
|
||||
}
|
||||
|
||||
static void _stderr_output(struct debug_target *target, const char *log)
|
||||
{
|
||||
fprintf(target->tgt_stdout.out, "%s", log);
|
||||
fflush(target->tgt_stdout.out);
|
||||
}
|
||||
|
||||
struct debug_target *debug_target_create(void)
|
||||
{
|
||||
struct debug_target *target;
|
||||
|
||||
target = talloc_zero(tall_dbg_ctx, struct debug_target);
|
||||
if (!target)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&target->entry);
|
||||
memcpy(target->categories, default_categories, sizeof(default_categories));
|
||||
target->use_color = 1;
|
||||
target->print_timestamp = 0;
|
||||
target->loglevel = 0;
|
||||
return target;
|
||||
}
|
||||
|
||||
struct debug_target *debug_target_create_stderr(void)
|
||||
{
|
||||
struct debug_target *target;
|
||||
|
||||
target = debug_target_create();
|
||||
if (!target)
|
||||
return NULL;
|
||||
|
||||
target->tgt_stdout.out = stderr;
|
||||
target->output = _stderr_output;
|
||||
return target;
|
||||
}
|
||||
|
||||
void debug_init(void)
|
||||
{
|
||||
tall_dbg_ctx = talloc_named_const(NULL, 1, "debug");
|
||||
}
|
||||
|
||||
@@ -303,6 +303,10 @@ int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line,
|
||||
|
||||
switch (type) {
|
||||
case E1INP_TS_TYPE_SIGN:
|
||||
if (line->driver)
|
||||
ts->sign.delay = line->driver->default_delay;
|
||||
else
|
||||
ts->sign.delay = 100000;
|
||||
INIT_LLIST_HEAD(&ts->sign.sign_links);
|
||||
break;
|
||||
case E1INP_TS_TYPE_TRAU:
|
||||
@@ -420,7 +424,17 @@ e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type,
|
||||
|
||||
void e1inp_sign_link_destroy(struct e1inp_sign_link *link)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
llist_del(&link->list);
|
||||
while (!llist_empty(&link->tx_list)) {
|
||||
msg = msgb_dequeue(&link->tx_list);
|
||||
msgb_free(msg);
|
||||
}
|
||||
|
||||
if (link->ts->type == E1INP_TS_TYPE_SIGN)
|
||||
bsc_del_timer(&link->ts->sign.tx_timer);
|
||||
|
||||
talloc_free(link);
|
||||
}
|
||||
|
||||
@@ -442,7 +456,7 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
debug_set_context(BSC_CTX_BTS, link->trx->bts);
|
||||
log_set_context(BSC_CTX_BTS, link->trx->bts);
|
||||
switch (link->type) {
|
||||
case E1INP_SIGN_OML:
|
||||
msg->trx = link->trx;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
|
||||
/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface
|
||||
* 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
|
||||
|
||||
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
|
||||
@@ -58,7 +58,7 @@ void *tall_locop_ctx;
|
||||
int gsm0408_loc_upd_acc(struct gsm_lchan *lchan, u_int32_t tmsi);
|
||||
static int gsm48_tx_simple(struct gsm_lchan *lchan,
|
||||
u_int8_t pdisc, u_int8_t msg_type);
|
||||
static void schedule_reject(struct gsm_lchan *lchan);
|
||||
static void schedule_reject(struct gsm_subscriber_connection *conn);
|
||||
|
||||
struct gsm_lai {
|
||||
u_int16_t mcc;
|
||||
@@ -96,35 +96,34 @@ static int authorize_subscriber(struct gsm_loc_updating_operation *loc,
|
||||
}
|
||||
}
|
||||
|
||||
static void release_loc_updating_req(struct gsm_lchan *lchan)
|
||||
static void release_loc_updating_req(struct gsm_subscriber_connection *conn)
|
||||
{
|
||||
if (!lchan->loc_operation)
|
||||
if (!conn->loc_operation)
|
||||
return;
|
||||
|
||||
bsc_del_timer(&lchan->loc_operation->updating_timer);
|
||||
talloc_free(lchan->loc_operation);
|
||||
lchan->loc_operation = 0;
|
||||
put_lchan(lchan);
|
||||
bsc_del_timer(&conn->loc_operation->updating_timer);
|
||||
talloc_free(conn->loc_operation);
|
||||
conn->loc_operation = 0;
|
||||
put_subscr_con(conn, 0);
|
||||
}
|
||||
|
||||
static void allocate_loc_updating_req(struct gsm_lchan *lchan)
|
||||
static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn)
|
||||
{
|
||||
use_lchan(lchan);
|
||||
release_loc_updating_req(lchan);
|
||||
use_subscr_con(conn);
|
||||
release_loc_updating_req(conn);
|
||||
|
||||
lchan->loc_operation = talloc_zero(tall_locop_ctx,
|
||||
conn->loc_operation = talloc_zero(tall_locop_ctx,
|
||||
struct gsm_loc_updating_operation);
|
||||
}
|
||||
|
||||
static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
|
||||
static int gsm0408_authorize(struct gsm_subscriber_connection *conn, struct msgb *msg)
|
||||
{
|
||||
if (authorize_subscriber(lchan->loc_operation, lchan->subscr)) {
|
||||
if (authorize_subscriber(conn->loc_operation, conn->subscr)) {
|
||||
int rc;
|
||||
|
||||
db_subscriber_alloc_tmsi(lchan->subscr);
|
||||
release_loc_updating_req(lchan);
|
||||
rc = gsm0408_loc_upd_acc(msg->lchan, lchan->subscr->tmsi);
|
||||
if (lchan->ts->trx->bts->network->send_mm_info) {
|
||||
db_subscriber_alloc_tmsi(conn->subscr);
|
||||
rc = gsm0408_loc_upd_acc(msg->lchan, conn->subscr->tmsi);
|
||||
if (msg->lchan->ts->trx->bts->network->send_mm_info) {
|
||||
/* send MM INFO with network name */
|
||||
rc = gsm48_tx_mm_info(msg->lchan);
|
||||
}
|
||||
@@ -132,10 +131,9 @@ static int gsm0408_authorize(struct gsm_lchan *lchan, struct msgb *msg)
|
||||
/* call subscr_update after putting the loc_upd_acc
|
||||
* in the transmit queue, since S_SUBSCR_ATTACHED might
|
||||
* trigger further action like SMS delivery */
|
||||
subscr_update(lchan->subscr, msg->trx->bts,
|
||||
subscr_update(conn->subscr, msg->trx->bts,
|
||||
GSM_SUBSCRIBER_UPDATE_ATTACHED);
|
||||
/* try to close channel ASAP */
|
||||
lchan_auto_release(lchan);
|
||||
release_loc_updating_req(conn);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -158,14 +156,14 @@ static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal,
|
||||
if (!lchan)
|
||||
return 0;
|
||||
|
||||
release_loc_updating_req(lchan);
|
||||
release_loc_updating_req(&lchan->conn);
|
||||
|
||||
/* Free all transactions that are associated with the released lchan */
|
||||
/* FIXME: this is not neccessarily the right thing to do, we should
|
||||
* only set trans->lchan to NULL and wait for another lchan to be
|
||||
* established to the same MM entity (phone/subscriber) */
|
||||
llist_for_each_entry_safe(trans, temp, &lchan->ts->trx->bts->network->trans_list, entry) {
|
||||
if (trans->lchan == lchan)
|
||||
if (trans->conn && trans->conn->lchan == lchan)
|
||||
trans_free(trans);
|
||||
}
|
||||
|
||||
@@ -175,11 +173,13 @@ static int gsm0408_handle_lchan_signal(unsigned int subsys, unsigned int signal,
|
||||
/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */
|
||||
int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
|
||||
{
|
||||
struct gsm_subscriber_connection *conn;
|
||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||
struct msgb *msg = gsm48_msgb_alloc();
|
||||
struct gsm48_hdr *gh;
|
||||
|
||||
msg->lchan = lchan;
|
||||
conn = &lchan->conn;
|
||||
|
||||
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
|
||||
gh->proto_discr = GSM48_PDISC_MM;
|
||||
@@ -187,8 +187,8 @@ int gsm0408_loc_upd_rej(struct gsm_lchan *lchan, u_int8_t cause)
|
||||
gh->data[0] = cause;
|
||||
|
||||
LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT "
|
||||
"LAC=%u BTS=%u\n", lchan->subscr ?
|
||||
subscr_name(lchan->subscr) : "unknown",
|
||||
"LAC=%u BTS=%u\n", conn->subscr ?
|
||||
subscr_name(conn->subscr) : "unknown",
|
||||
lchan->ts->trx->bts->location_area_code, lchan->ts->trx->bts->nr);
|
||||
|
||||
counter_inc(bts->network->stats.loc_upd_resp.reject);
|
||||
@@ -245,6 +245,7 @@ static int mm_tx_identity_req(struct gsm_lchan *lchan, u_int8_t id_type)
|
||||
/* Parse Chapter 9.2.11 Identity Response */
|
||||
static int mm_rx_id_resp(struct msgb *msg)
|
||||
{
|
||||
struct gsm_subscriber_connection *conn;
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
struct gsm_lchan *lchan = msg->lchan;
|
||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||
@@ -256,51 +257,53 @@ static int mm_rx_id_resp(struct msgb *msg)
|
||||
DEBUGP(DMM, "IDENTITY RESPONSE: mi_type=0x%02x MI(%s)\n",
|
||||
mi_type, mi_string);
|
||||
|
||||
conn = &lchan->conn;
|
||||
|
||||
dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data);
|
||||
|
||||
switch (mi_type) {
|
||||
case GSM_MI_TYPE_IMSI:
|
||||
/* look up subscriber based on IMSI, create if not found */
|
||||
if (!lchan->subscr) {
|
||||
lchan->subscr = subscr_get_by_imsi(net, mi_string);
|
||||
if (!lchan->subscr)
|
||||
lchan->subscr = db_create_subscriber(net, mi_string);
|
||||
if (!conn->subscr) {
|
||||
conn->subscr = subscr_get_by_imsi(net, mi_string);
|
||||
if (!conn->subscr)
|
||||
conn->subscr = db_create_subscriber(net, mi_string);
|
||||
}
|
||||
if (lchan->loc_operation)
|
||||
lchan->loc_operation->waiting_for_imsi = 0;
|
||||
if (conn->loc_operation)
|
||||
conn->loc_operation->waiting_for_imsi = 0;
|
||||
break;
|
||||
case GSM_MI_TYPE_IMEI:
|
||||
case GSM_MI_TYPE_IMEISV:
|
||||
/* update subscribe <-> IMEI mapping */
|
||||
if (lchan->subscr) {
|
||||
db_subscriber_assoc_imei(lchan->subscr, mi_string);
|
||||
db_sync_equipment(&lchan->subscr->equipment);
|
||||
if (conn->subscr) {
|
||||
db_subscriber_assoc_imei(conn->subscr, mi_string);
|
||||
db_sync_equipment(&conn->subscr->equipment);
|
||||
}
|
||||
if (lchan->loc_operation)
|
||||
lchan->loc_operation->waiting_for_imei = 0;
|
||||
if (conn->loc_operation)
|
||||
conn->loc_operation->waiting_for_imei = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if we can let the mobile station enter */
|
||||
return gsm0408_authorize(lchan, msg);
|
||||
return gsm0408_authorize(conn, msg);
|
||||
}
|
||||
|
||||
|
||||
static void loc_upd_rej_cb(void *data)
|
||||
{
|
||||
struct gsm_lchan *lchan = data;
|
||||
struct gsm_subscriber_connection *conn = data;
|
||||
struct gsm_lchan *lchan = conn->lchan;
|
||||
struct gsm_bts *bts = lchan->ts->trx->bts;
|
||||
|
||||
release_loc_updating_req(lchan);
|
||||
gsm0408_loc_upd_rej(lchan, bts->network->reject_cause);
|
||||
lchan_auto_release(lchan);
|
||||
release_loc_updating_req(conn);
|
||||
}
|
||||
|
||||
static void schedule_reject(struct gsm_lchan *lchan)
|
||||
static void schedule_reject(struct gsm_subscriber_connection *conn)
|
||||
{
|
||||
lchan->loc_operation->updating_timer.cb = loc_upd_rej_cb;
|
||||
lchan->loc_operation->updating_timer.data = lchan;
|
||||
bsc_schedule_timer(&lchan->loc_operation->updating_timer, 5, 0);
|
||||
conn->loc_operation->updating_timer.cb = loc_upd_rej_cb;
|
||||
conn->loc_operation->updating_timer.data = conn;
|
||||
bsc_schedule_timer(&conn->loc_operation->updating_timer, 5, 0);
|
||||
}
|
||||
|
||||
static const char *lupd_name(u_int8_t type)
|
||||
@@ -320,6 +323,7 @@ static const char *lupd_name(u_int8_t type)
|
||||
/* Chapter 9.2.15: Receive Location Updating Request */
|
||||
static int mm_rx_loc_upd_req(struct msgb *msg)
|
||||
{
|
||||
struct gsm_subscriber_connection *conn;
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
struct gsm48_loc_upd_req *lu;
|
||||
struct gsm_subscriber *subscr = NULL;
|
||||
@@ -330,6 +334,7 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
|
||||
int rc;
|
||||
|
||||
lu = (struct gsm48_loc_upd_req *) gh->data;
|
||||
conn = &lchan->conn;
|
||||
|
||||
mi_type = lu->mi[0] & GSM_MI_TYPE_MASK;
|
||||
|
||||
@@ -356,21 +361,21 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
|
||||
* Pseudo Spoof detection: Just drop a second/concurrent
|
||||
* location updating request.
|
||||
*/
|
||||
if (lchan->loc_operation) {
|
||||
if (conn->loc_operation) {
|
||||
DEBUGPC(DMM, "ignoring request due an existing one: %p.\n",
|
||||
lchan->loc_operation);
|
||||
conn->loc_operation);
|
||||
gsm0408_loc_upd_rej(lchan, GSM48_REJECT_PROTOCOL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
allocate_loc_updating_req(lchan);
|
||||
allocate_loc_updating_req(&lchan->conn);
|
||||
|
||||
switch (mi_type) {
|
||||
case GSM_MI_TYPE_IMSI:
|
||||
DEBUGPC(DMM, "\n");
|
||||
/* we always want the IMEI, too */
|
||||
rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
|
||||
lchan->loc_operation->waiting_for_imei = 1;
|
||||
conn->loc_operation->waiting_for_imei = 1;
|
||||
|
||||
/* look up subscriber based on IMSI, create if not found */
|
||||
subscr = subscr_get_by_imsi(bts->network, mi_string);
|
||||
@@ -382,7 +387,7 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
|
||||
DEBUGPC(DMM, "\n");
|
||||
/* we always want the IMEI, too */
|
||||
rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMEI);
|
||||
lchan->loc_operation->waiting_for_imei = 1;
|
||||
conn->loc_operation->waiting_for_imei = 1;
|
||||
|
||||
/* look up the subscriber based on TMSI, request IMSI if it fails */
|
||||
subscr = subscr_get_by_tmsi(bts->network,
|
||||
@@ -390,7 +395,7 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
|
||||
if (!subscr) {
|
||||
/* send IDENTITY REQUEST message to get IMSI */
|
||||
rc = mm_tx_identity_req(lchan, GSM_MI_TYPE_IMSI);
|
||||
lchan->loc_operation->waiting_for_imsi = 1;
|
||||
conn->loc_operation->waiting_for_imsi = 1;
|
||||
}
|
||||
break;
|
||||
case GSM_MI_TYPE_IMEI:
|
||||
@@ -404,7 +409,7 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
|
||||
}
|
||||
|
||||
/* schedule the reject timer */
|
||||
schedule_reject(lchan);
|
||||
schedule_reject(conn);
|
||||
|
||||
if (!subscr) {
|
||||
DEBUGPC(DRR, "<- Can't find any subscriber for this ID\n");
|
||||
@@ -412,12 +417,12 @@ static int mm_rx_loc_upd_req(struct msgb *msg)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lchan->subscr = subscr;
|
||||
lchan->subscr->equipment.classmark1 = lu->classmark1;
|
||||
conn->subscr = subscr;
|
||||
conn->subscr->equipment.classmark1 = lu->classmark1;
|
||||
|
||||
/* check if we can let the subscriber into our network immediately
|
||||
* or if we need to wait for identity responses. */
|
||||
return gsm0408_authorize(lchan, msg);
|
||||
return gsm0408_authorize(conn, msg);
|
||||
}
|
||||
|
||||
#if 0
|
||||
@@ -566,7 +571,7 @@ static int gsm48_tx_mm_serv_ack(struct gsm_lchan *lchan)
|
||||
}
|
||||
|
||||
/* 9.2.6 CM service reject */
|
||||
static int gsm48_tx_mm_serv_rej(struct gsm_lchan *lchan,
|
||||
static int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
|
||||
enum gsm48_reject_value value)
|
||||
{
|
||||
struct msgb *msg = gsm48_msgb_alloc();
|
||||
@@ -574,8 +579,8 @@ static int gsm48_tx_mm_serv_rej(struct gsm_lchan *lchan,
|
||||
|
||||
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
|
||||
|
||||
msg->lchan = lchan;
|
||||
use_lchan(lchan);
|
||||
msg->lchan = conn->lchan;
|
||||
use_subscr_con(conn);
|
||||
|
||||
gh->proto_discr = GSM48_PDISC_MM;
|
||||
gh->msg_type = GSM48_MT_MM_CM_SERV_REJ;
|
||||
@@ -613,20 +618,20 @@ static int gsm48_rx_mm_serv_req(struct msgb *msg)
|
||||
DEBUGP(DMM, "<- CM SERVICE REQUEST ");
|
||||
if (msg->data_len < sizeof(struct gsm48_service_request*)) {
|
||||
DEBUGPC(DMM, "wrong sized message\n");
|
||||
return gsm48_tx_mm_serv_rej(msg->lchan,
|
||||
return gsm48_tx_mm_serv_rej(&msg->lchan->conn,
|
||||
GSM48_REJECT_INCORRECT_MESSAGE);
|
||||
}
|
||||
|
||||
if (msg->data_len < req->mi_len + 6) {
|
||||
DEBUGPC(DMM, "does not fit in packet\n");
|
||||
return gsm48_tx_mm_serv_rej(msg->lchan,
|
||||
return gsm48_tx_mm_serv_rej(&msg->lchan->conn,
|
||||
GSM48_REJECT_INCORRECT_MESSAGE);
|
||||
}
|
||||
|
||||
mi_type = mi[0] & GSM_MI_TYPE_MASK;
|
||||
if (mi_type != GSM_MI_TYPE_TMSI) {
|
||||
DEBUGPC(DMM, "mi_type is not TMSI: %d\n", mi_type);
|
||||
return gsm48_tx_mm_serv_rej(msg->lchan,
|
||||
return gsm48_tx_mm_serv_rej(&msg->lchan->conn,
|
||||
GSM48_REJECT_INCORRECT_MESSAGE);
|
||||
}
|
||||
|
||||
@@ -644,12 +649,12 @@ static int gsm48_rx_mm_serv_req(struct msgb *msg)
|
||||
|
||||
/* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */
|
||||
if (!subscr)
|
||||
return gsm48_tx_mm_serv_rej(msg->lchan,
|
||||
return gsm48_tx_mm_serv_rej(&msg->lchan->conn,
|
||||
GSM48_REJECT_IMSI_UNKNOWN_IN_HLR);
|
||||
|
||||
if (!msg->lchan->subscr)
|
||||
msg->lchan->subscr = subscr;
|
||||
else if (msg->lchan->subscr == subscr)
|
||||
if (!msg->lchan->conn.subscr)
|
||||
msg->lchan->conn.subscr = subscr;
|
||||
else if (msg->lchan->conn.subscr == subscr)
|
||||
subscr_put(subscr); /* lchan already has a ref, don't need another one */
|
||||
else {
|
||||
DEBUGP(DMM, "<- CM Channel already owned by someone else?\n");
|
||||
@@ -713,8 +718,6 @@ static int gsm48_rx_mm_imsi_detach_ind(struct msgb *msg)
|
||||
* imagine an IMSI DETACH happening during an active call! */
|
||||
|
||||
/* subscriber is detached: should we release lchan? */
|
||||
lchan_auto_release(msg->lchan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -749,8 +752,8 @@ static int gsm0408_rcv_mm(struct msgb *msg)
|
||||
break;
|
||||
case GSM48_MT_MM_TMSI_REALL_COMPL:
|
||||
DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n",
|
||||
msg->lchan->subscr ?
|
||||
subscr_name(msg->lchan->subscr) :
|
||||
msg->lchan->conn.subscr ?
|
||||
subscr_name(msg->lchan->conn.subscr) :
|
||||
"unknown subscriber");
|
||||
break;
|
||||
case GSM48_MT_MM_IMSI_DETACH_IND:
|
||||
@@ -815,7 +818,7 @@ static int gsm48_rx_rr_pag_resp(struct msgb *msg)
|
||||
static int gsm48_rx_rr_classmark(struct msgb *msg)
|
||||
{
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
struct gsm_subscriber *subscr = msg->lchan->subscr;
|
||||
struct gsm_subscriber *subscr = msg->lchan->conn.subscr;
|
||||
unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh);
|
||||
u_int8_t cm2_len, cm3_len = 0;
|
||||
u_int8_t *cm2, *cm3 = NULL;
|
||||
@@ -863,7 +866,7 @@ static int gsm48_rx_rr_status(struct msgb *msg)
|
||||
{
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
|
||||
DEBUGP(DRR, "STATUS rr_cause = %s\n",
|
||||
DEBUGP(DRR, "STATUS rr_cause = %s\n",
|
||||
rr_cause_name(gh->data[0]));
|
||||
|
||||
return 0;
|
||||
@@ -896,7 +899,7 @@ static int gsm48_rx_rr_app_info(struct msgb *msg)
|
||||
DEBUGP(DNM, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s",
|
||||
apdu_id_flags, apdu_len, hexdump(apdu_data, apdu_len));
|
||||
|
||||
return db_apdu_blob_store(msg->lchan->subscr, apdu_id_flags, apdu_len, apdu_data);
|
||||
return db_apdu_blob_store(msg->lchan->conn.subscr, apdu_id_flags, apdu_len, apdu_data);
|
||||
}
|
||||
|
||||
/* Chapter 9.1.16 Handover complete */
|
||||
@@ -1008,7 +1011,8 @@ static void new_cc_state(struct gsm_trans *trans, int state)
|
||||
return;
|
||||
|
||||
DEBUGP(DCC, "new state %s -> %s\n",
|
||||
cc_state_names[trans->cc.state], cc_state_names[state]);
|
||||
gsm48_cc_state_name(trans->cc.state),
|
||||
gsm48_cc_state_name(state));
|
||||
|
||||
trans->cc.state = state;
|
||||
}
|
||||
@@ -1054,19 +1058,19 @@ static void gsm48_stop_cc_timer(struct gsm_trans *trans)
|
||||
trans->cc.Tcurrent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans,
|
||||
int msg_type, struct gsm_mncc *mncc)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
if (trans)
|
||||
if (trans->lchan)
|
||||
if (trans->conn)
|
||||
DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) "
|
||||
"Sending '%s' to MNCC.\n",
|
||||
trans->lchan->ts->trx->bts->nr,
|
||||
trans->lchan->ts->trx->nr,
|
||||
trans->lchan->ts->nr, trans->transaction_id,
|
||||
trans->conn->lchan->ts->trx->bts->nr,
|
||||
trans->conn->lchan->ts->trx->nr,
|
||||
trans->conn->lchan->ts->nr, trans->transaction_id,
|
||||
(trans->subscr)?(trans->subscr->extension):"-",
|
||||
get_mncc_name(msg_type));
|
||||
else
|
||||
@@ -1115,12 +1119,12 @@ void _gsm48_cc_trans_free(struct gsm_trans *trans)
|
||||
}
|
||||
if (trans->cc.state != GSM_CSTATE_NULL)
|
||||
new_cc_state(trans, GSM_CSTATE_NULL);
|
||||
if (trans->lchan)
|
||||
trau_mux_unmap(&trans->lchan->ts->e1_link, trans->callref);
|
||||
if (trans->conn)
|
||||
trau_mux_unmap(&trans->conn->lchan->ts->e1_link, trans->callref);
|
||||
}
|
||||
|
||||
static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg);
|
||||
|
||||
|
||||
/* call-back from paging the B-end of the connection */
|
||||
static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
|
||||
struct msgb *msg, void *_lchan, void *param)
|
||||
@@ -1132,7 +1136,7 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
|
||||
|
||||
if (hooknum != GSM_HOOK_RR_PAGING)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
if (!subscr)
|
||||
return -EINVAL;
|
||||
net = subscr->net;
|
||||
@@ -1143,7 +1147,7 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
|
||||
|
||||
/* check all tranactions (without lchan) for subscriber */
|
||||
llist_for_each_entry_safe(transt, tmp, &net->trans_list, entry) {
|
||||
if (transt->subscr != subscr || transt->lchan)
|
||||
if (transt->subscr != subscr || transt->conn)
|
||||
continue;
|
||||
switch (event) {
|
||||
case GSM_PAGING_SUCCEEDED:
|
||||
@@ -1152,9 +1156,9 @@ static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event,
|
||||
DEBUGP(DCC, "Paging subscr %s succeeded!\n",
|
||||
subscr->extension);
|
||||
/* Assign lchan */
|
||||
if (!transt->lchan) {
|
||||
transt->lchan = lchan;
|
||||
use_lchan(lchan);
|
||||
if (!transt->conn) {
|
||||
transt->conn = &lchan->conn;
|
||||
use_subscr_con(transt->conn);
|
||||
}
|
||||
/* send SETUP request to called party */
|
||||
gsm48_cc_tx_setup(transt, &transt->cc.msg);
|
||||
@@ -1199,7 +1203,7 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
|
||||
* a tch_recv_mncc request pending */
|
||||
net = lchan->ts->trx->bts->network;
|
||||
llist_for_each_entry(trans, &net->trans_list, entry) {
|
||||
if (trans->lchan == lchan && trans->tch_recv) {
|
||||
if (trans->conn && trans->conn->lchan == lchan && trans->tch_recv) {
|
||||
DEBUGP(DCC, "pending tch_recv_mncc request\n");
|
||||
tch_recv_mncc(net, trans->callref, 1);
|
||||
}
|
||||
@@ -1272,11 +1276,11 @@ static int tch_bridge(struct gsm_network *net, u_int32_t *refs)
|
||||
if (!trans1 || !trans2)
|
||||
return -EIO;
|
||||
|
||||
if (!trans1->lchan || !trans2->lchan)
|
||||
if (!trans1->conn || !trans2->conn)
|
||||
return -EIO;
|
||||
|
||||
/* through-connect channel */
|
||||
return tch_map(trans1->lchan, trans2->lchan);
|
||||
return tch_map(trans1->conn->lchan, trans2->conn->lchan);
|
||||
}
|
||||
|
||||
/* enable receive of channels to MNCC upqueue */
|
||||
@@ -1291,9 +1295,9 @@ static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable)
|
||||
trans = trans_find_by_callref(net, callref);
|
||||
if (!trans)
|
||||
return -EIO;
|
||||
if (!trans->lchan)
|
||||
if (!trans->conn)
|
||||
return 0;
|
||||
lchan = trans->lchan;
|
||||
lchan = trans->conn->lchan;
|
||||
bts = lchan->ts->trx->bts;
|
||||
|
||||
switch (bts->type) {
|
||||
@@ -2060,7 +2064,6 @@ static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg)
|
||||
MNCC_REL_CNF, &rel);
|
||||
/* FIXME: in case of multiple calls, we can't simply
|
||||
* hang up here ! */
|
||||
lchan_auto_release(msg->lchan);
|
||||
break;
|
||||
default:
|
||||
rc = mncc_recvmsg(trans->subscr->net, trans,
|
||||
@@ -2489,7 +2492,7 @@ static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
|
||||
{
|
||||
struct gsm_mncc *mode = arg;
|
||||
|
||||
return gsm48_lchan_modify(trans->lchan, mode->lchan_mode);
|
||||
return gsm48_lchan_modify(trans->conn->lchan, mode->lchan_mode);
|
||||
}
|
||||
|
||||
static struct downstate {
|
||||
@@ -2575,18 +2578,18 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
|
||||
trans = trans_find_by_callref(net, data->callref);
|
||||
if (!trans)
|
||||
return -EIO;
|
||||
if (!trans->lchan)
|
||||
if (!trans->conn)
|
||||
return 0;
|
||||
if (trans->lchan->type != GSM_LCHAN_TCH_F)
|
||||
if (trans->conn->lchan->type != GSM_LCHAN_TCH_F)
|
||||
return 0;
|
||||
bts = trans->lchan->ts->trx->bts;
|
||||
bts = trans->conn->lchan->ts->trx->bts;
|
||||
switch (bts->type) {
|
||||
case GSM_BTS_TYPE_NANOBTS:
|
||||
if (!trans->lchan->abis_ip.rtp_socket)
|
||||
if (!trans->conn->lchan->abis_ip.rtp_socket)
|
||||
return 0;
|
||||
return rtp_send_frame(trans->lchan->abis_ip.rtp_socket, arg);
|
||||
return rtp_send_frame(trans->conn->lchan->abis_ip.rtp_socket, arg);
|
||||
case GSM_BTS_TYPE_BS11:
|
||||
return trau_send_frame(trans->lchan, arg);
|
||||
return trau_send_frame(trans->conn->lchan, arg);
|
||||
default:
|
||||
DEBUGP(DCC, "Unknown BTS type %u\n", bts->type);
|
||||
}
|
||||
@@ -2664,6 +2667,7 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
|
||||
}
|
||||
/* Find lchan */
|
||||
lchan = lchan_for_subscr(subscr);
|
||||
|
||||
/* If subscriber has no lchan */
|
||||
if (!lchan) {
|
||||
/* find transaction with this subscriber already paging */
|
||||
@@ -2691,16 +2695,18 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
|
||||
return 0;
|
||||
}
|
||||
/* Assign lchan */
|
||||
trans->lchan = lchan;
|
||||
use_lchan(lchan);
|
||||
trans->conn = &lchan->conn;
|
||||
use_subscr_con(trans->conn);
|
||||
subscr_put(subscr);
|
||||
}
|
||||
lchan = trans->lchan;
|
||||
|
||||
if (trans->conn)
|
||||
lchan = trans->conn->lchan;
|
||||
|
||||
/* if paging did not respond yet */
|
||||
if (!lchan) {
|
||||
DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) "
|
||||
"Received '%s' from MNCC in paging state\n",
|
||||
"Received '%s' from MNCC in paging state\n",
|
||||
(trans->subscr)?(trans->subscr->extension):"-",
|
||||
get_mncc_name(msg_type));
|
||||
mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU,
|
||||
@@ -2718,9 +2724,9 @@ int mncc_send(struct gsm_network *net, int msg_type, void *arg)
|
||||
"Received '%s' from MNCC in state %d (%s)\n",
|
||||
lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
|
||||
trans->transaction_id,
|
||||
(lchan->subscr)?(lchan->subscr->extension):"-",
|
||||
(trans->conn->subscr)?(trans->conn->subscr->extension):"-",
|
||||
get_mncc_name(msg_type), trans->cc.state,
|
||||
cc_state_names[trans->cc.state]);
|
||||
gsm48_cc_state_name(trans->cc.state));
|
||||
|
||||
/* Find function for current state and message */
|
||||
for (i = 0; i < DOWNSLLEN; i++)
|
||||
@@ -2755,7 +2761,7 @@ static struct datastate {
|
||||
GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf},
|
||||
{SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */
|
||||
GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting},
|
||||
{SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */
|
||||
{SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */
|
||||
GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect},
|
||||
/* signalling during call */
|
||||
{ALL_STATES - SBIT(GSM_CSTATE_NULL),
|
||||
@@ -2794,6 +2800,7 @@ static struct datastate {
|
||||
|
||||
static int gsm0408_rcv_cc(struct msgb *msg)
|
||||
{
|
||||
struct gsm_subscriber_connection *conn;
|
||||
struct gsm48_hdr *gh = msgb_l3(msg);
|
||||
u_int8_t msg_type = gh->msg_type & 0xbf;
|
||||
u_int8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */
|
||||
@@ -2805,23 +2812,25 @@ static int gsm0408_rcv_cc(struct msgb *msg)
|
||||
DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
conn = &lchan->conn;
|
||||
|
||||
/* Find transaction */
|
||||
trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_CC, transaction_id);
|
||||
trans = trans_find_by_id(conn->subscr, GSM48_PDISC_CC, transaction_id);
|
||||
|
||||
DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) "
|
||||
"Received '%s' from MS in state %d (%s)\n",
|
||||
lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,
|
||||
transaction_id, (lchan->subscr)?(lchan->subscr->extension):"-",
|
||||
gsm48_cc_msg_names[msg_type], trans?(trans->cc.state):0,
|
||||
cc_state_names[trans?(trans->cc.state):0]);
|
||||
transaction_id, (conn->subscr)?(conn->subscr->extension):"-",
|
||||
gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0,
|
||||
gsm48_cc_state_name(trans?(trans->cc.state):0));
|
||||
|
||||
/* Create transaction */
|
||||
if (!trans) {
|
||||
DEBUGP(DCC, "Unknown transaction ID %x, "
|
||||
"creating new trans.\n", transaction_id);
|
||||
/* Create transaction */
|
||||
trans = trans_alloc(lchan->subscr, GSM48_PDISC_CC,
|
||||
trans = trans_alloc(conn->subscr, GSM48_PDISC_CC,
|
||||
transaction_id, new_callref++);
|
||||
if (!trans) {
|
||||
DEBUGP(DCC, "No memory for trans.\n");
|
||||
@@ -2831,8 +2840,8 @@ static int gsm0408_rcv_cc(struct msgb *msg)
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Assign transaction */
|
||||
trans->lchan = lchan;
|
||||
use_lchan(lchan);
|
||||
trans->conn = &lchan->conn;
|
||||
use_subscr_con(trans->conn);
|
||||
}
|
||||
|
||||
/* find function for current state and message */
|
||||
|
||||
@@ -57,7 +57,7 @@ int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans)
|
||||
* work that the caller no longer has to do */
|
||||
if (trans) {
|
||||
gh->proto_discr = trans->protocol | (trans->transaction_id << 4);
|
||||
msg->lchan = trans->lchan;
|
||||
msg->lchan = trans->conn->lchan;
|
||||
}
|
||||
|
||||
if (msg->lchan) {
|
||||
@@ -68,7 +68,7 @@ int gsm48_sendmsg(struct msgb *msg, struct gsm_trans *trans)
|
||||
"Sending '%s' to MS.\n", msg->trx->bts->nr,
|
||||
msg->trx->nr, msg->lchan->ts->nr,
|
||||
gh->proto_discr & 0xf0,
|
||||
gsm48_cc_msg_names[gh->msg_type & 0x3f]);
|
||||
gsm48_cc_msg_name(gh->msg_type));
|
||||
else
|
||||
DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) "
|
||||
"Sending 0x%02x to MS.\n", msg->trx->bts->nr,
|
||||
@@ -164,13 +164,46 @@ static const enum gsm_chreq_reason_t reason_by_chreq[] = {
|
||||
[CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER,
|
||||
};
|
||||
|
||||
enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
|
||||
/* verify that the two tables match */
|
||||
static_assert(sizeof(ctype_by_chreq) ==
|
||||
sizeof(((struct gsm_network *) NULL)->ctype_by_chreq), assert_size);
|
||||
|
||||
/*
|
||||
* Update channel types for request based on policy. E.g. in the
|
||||
* case of a TCH/H network/bsc use TCH/H for the emergency calls,
|
||||
* for early assignment assign a SDCCH and some other options.
|
||||
*/
|
||||
void gsm_net_update_ctype(struct gsm_network *network)
|
||||
{
|
||||
/* copy over the data */
|
||||
memcpy(network->ctype_by_chreq, ctype_by_chreq, sizeof(ctype_by_chreq));
|
||||
|
||||
/*
|
||||
* Use TCH/H for emergency calls when this cell allows TCH/H. Maybe it
|
||||
* is better to iterate over the BTS/TRX and check if no TCH/F is available
|
||||
* and then set it to TCH/H.
|
||||
*/
|
||||
if (network->neci)
|
||||
network->ctype_by_chreq[CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_H;
|
||||
|
||||
if (network->pag_any_tch) {
|
||||
if (network->neci) {
|
||||
network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_H;
|
||||
network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_H;
|
||||
} else {
|
||||
network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_F;
|
||||
network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_F;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, u_int8_t ra)
|
||||
{
|
||||
int i;
|
||||
int length;
|
||||
const struct chreq *chreq;
|
||||
|
||||
if (neci) {
|
||||
if (network->neci) {
|
||||
chreq = chreq_type_neci1;
|
||||
length = ARRAY_SIZE(chreq_type_neci1);
|
||||
} else {
|
||||
@@ -182,13 +215,13 @@ enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
|
||||
for (i = 0; i < length; i++) {
|
||||
const struct chreq *chr = &chreq[i];
|
||||
if ((ra & chr->mask) == chr->val)
|
||||
return ctype_by_chreq[chr->type];
|
||||
return network->ctype_by_chreq[chr->type];
|
||||
}
|
||||
LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra);
|
||||
return GSM_LCHAN_SDCCH;
|
||||
}
|
||||
|
||||
enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
|
||||
enum gsm_chreq_reason_t get_reason_by_chreq(u_int8_t ra, int neci)
|
||||
{
|
||||
int i;
|
||||
int length;
|
||||
@@ -236,50 +269,6 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan)
|
||||
return rsl_deact_sacch(lchan);
|
||||
}
|
||||
|
||||
/* Convert Mobile Identity (10.5.1.4) to string */
|
||||
int gsm48_mi_to_string(char *string, const int str_len, const u_int8_t *mi, const int mi_len)
|
||||
{
|
||||
int i;
|
||||
u_int8_t mi_type;
|
||||
char *str_cur = string;
|
||||
u_int32_t tmsi;
|
||||
|
||||
mi_type = mi[0] & GSM_MI_TYPE_MASK;
|
||||
|
||||
switch (mi_type) {
|
||||
case GSM_MI_TYPE_NONE:
|
||||
break;
|
||||
case GSM_MI_TYPE_TMSI:
|
||||
/* Table 10.5.4.3, reverse generate_mid_from_tmsi */
|
||||
if (mi_len == GSM48_TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) {
|
||||
memcpy(&tmsi, &mi[1], 4);
|
||||
tmsi = ntohl(tmsi);
|
||||
return snprintf(string, str_len, "%u", tmsi);
|
||||
}
|
||||
break;
|
||||
case GSM_MI_TYPE_IMSI:
|
||||
case GSM_MI_TYPE_IMEI:
|
||||
case GSM_MI_TYPE_IMEISV:
|
||||
*str_cur++ = bcd2char(mi[0] >> 4);
|
||||
|
||||
for (i = 1; i < mi_len; i++) {
|
||||
if (str_cur + 2 >= string + str_len)
|
||||
return str_cur - string;
|
||||
*str_cur++ = bcd2char(mi[i] & 0xf);
|
||||
/* skip last nibble in last input byte when GSM_EVEN */
|
||||
if( (i != mi_len-1) || (mi[0] & GSM_MI_ODD))
|
||||
*str_cur++ = bcd2char(mi[i] >> 4);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*str_cur++ = '\0';
|
||||
|
||||
return str_cur - string;
|
||||
}
|
||||
|
||||
|
||||
int send_siemens_mrpci(struct gsm_lchan *lchan,
|
||||
u_int8_t *classmark2_lv)
|
||||
{
|
||||
@@ -316,23 +305,23 @@ int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr)
|
||||
if (is_siemens_bts(bts))
|
||||
send_siemens_mrpci(msg->lchan, classmark2_lv);
|
||||
|
||||
if (!msg->lchan->subscr) {
|
||||
msg->lchan->subscr = subscr;
|
||||
} else if (msg->lchan->subscr != subscr) {
|
||||
if (!msg->lchan->conn.subscr) {
|
||||
msg->lchan->conn.subscr = subscr;
|
||||
} else if (msg->lchan->conn.subscr != subscr) {
|
||||
LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n");
|
||||
subscr_put(subscr);
|
||||
return -EINVAL;
|
||||
} else {
|
||||
DEBUGP(DRR, "<- Channel already owned by us\n");
|
||||
subscr_put(subscr);
|
||||
subscr = msg->lchan->subscr;
|
||||
subscr = msg->lchan->conn.subscr;
|
||||
}
|
||||
|
||||
sig_data.subscr = subscr;
|
||||
sig_data.bts = msg->lchan->ts->trx->bts;
|
||||
sig_data.lchan = msg->lchan;
|
||||
|
||||
bts->network->stats.paging.completed++;
|
||||
counter_inc(bts->network->stats.paging.completed);
|
||||
|
||||
dispatch_signal(SS_PAGING, S_PAGING_SUCCEEDED, &sig_data);
|
||||
|
||||
@@ -626,4 +615,3 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -63,7 +64,7 @@ static const struct value_string cp_cause_strs[] = {
|
||||
{ GSM411_CP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" },
|
||||
{ GSM411_CP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" },
|
||||
{ GSM411_CP_CAUSE_MSGTYPE_NOTEXIST, "Message Type doesn't exist" },
|
||||
{ GSM411_CP_CAUSE_MSG_INCOMP_STATE,
|
||||
{ GSM411_CP_CAUSE_MSG_INCOMP_STATE,
|
||||
"Message incompatible with protocol state" },
|
||||
{ GSM411_CP_CAUSE_IE_NOTEXIST, "IE does not exist" },
|
||||
{ GSM411_CP_CAUSE_PROTOCOL_ERR, "Protocol Error" },
|
||||
@@ -121,16 +122,11 @@ struct msgb *gsm411_msgb_alloc(void)
|
||||
"GSM 04.11");
|
||||
}
|
||||
|
||||
static int gsm411_sendmsg(struct msgb *msg, u_int8_t link_id)
|
||||
static int gsm411_sendmsg(struct gsm_subscriber_connection *conn, struct msgb *msg, u_int8_t link_id)
|
||||
{
|
||||
if (msg->lchan)
|
||||
msg->trx = msg->lchan->ts->trx;
|
||||
|
||||
msg->l3h = msg->data;
|
||||
|
||||
DEBUGP(DSMS, "GSM4.11 TX %s\n", hexdump(msg->data, msg->len));
|
||||
|
||||
return rsl_data_request(msg, link_id);
|
||||
msg->l3h = msg->data;
|
||||
return gsm0808_submit_dtap(conn, msg, link_id);
|
||||
}
|
||||
|
||||
/* SMC TC1* is expired */
|
||||
@@ -154,9 +150,6 @@ static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
|
||||
gh->proto_discr = trans->protocol | (trans->transaction_id<<4);
|
||||
gh->msg_type = msg_type;
|
||||
|
||||
/* assign the outgoing lchan */
|
||||
msg->lchan = trans->lchan;
|
||||
|
||||
/* mobile originating */
|
||||
switch (gh->msg_type) {
|
||||
case GSM411_MT_CP_DATA:
|
||||
@@ -179,7 +172,7 @@ static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans,
|
||||
|
||||
DEBUGPC(DSMS, "trans=%x\n", trans->transaction_id);
|
||||
|
||||
return gsm411_sendmsg(msg, trans->sms.link_id);
|
||||
return gsm411_sendmsg(trans->conn, msg, trans->sms.link_id);
|
||||
}
|
||||
|
||||
/* Prefix msg with a RP-DATA header and send as CP-DATA */
|
||||
@@ -215,7 +208,7 @@ static u_int8_t unbcdify(u_int8_t value)
|
||||
u_int8_t ret;
|
||||
|
||||
if ((value & 0x0F) > 9 || (value >> 4) > 9)
|
||||
LOGP(DSMS, LOGL_ERROR,
|
||||
LOGP(DSMS, LOGL_ERROR,
|
||||
"unbcdify got too big nibble: 0x%02X\n", value);
|
||||
|
||||
ret = (value&0x0F)*10;
|
||||
@@ -509,11 +502,10 @@ static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms)
|
||||
return msg->len - old_msg_len;
|
||||
}
|
||||
|
||||
/* process an incoming TPDU (called from RP-DATA)
|
||||
* return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */
|
||||
static int gsm340_rx_tpdu(struct msgb *msg)
|
||||
/* process an incoming TPDU (called from RP-DATA)
|
||||
* return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */
|
||||
static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *msg)
|
||||
{
|
||||
struct gsm_bts *bts = msg->lchan->ts->trx->bts;
|
||||
u_int8_t *smsp = msgb_sms(msg);
|
||||
struct gsm_sms *gsms;
|
||||
u_int8_t sms_mti, sms_mms, sms_vpf, sms_alphabet, sms_rp;
|
||||
@@ -522,7 +514,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
|
||||
u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */
|
||||
int rc = 0;
|
||||
|
||||
counter_inc(bts->network->stats.sms.submitted);
|
||||
counter_inc(conn->bts->network->stats.sms.submitted);
|
||||
|
||||
gsms = sms_alloc();
|
||||
if (!gsms)
|
||||
@@ -575,7 +567,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
|
||||
sms_vp = 0;
|
||||
break;
|
||||
default:
|
||||
LOGP(DSMS, LOGL_NOTICE,
|
||||
LOGP(DSMS, LOGL_NOTICE,
|
||||
"SMS Validity period not implemented: 0x%02x\n", sms_vpf);
|
||||
return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER;
|
||||
}
|
||||
@@ -594,7 +586,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
|
||||
}
|
||||
}
|
||||
|
||||
gsms->sender = subscr_get(msg->lchan->subscr);
|
||||
gsms->sender = subscr_get(msg->lchan->conn.subscr);
|
||||
|
||||
LOGP(DSMS, LOGL_INFO, "RX SMS: Sender: %s, MTI: 0x%02x, VPF: 0x%02x, "
|
||||
"MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, "
|
||||
@@ -602,7 +594,7 @@ static int gsm340_rx_tpdu(struct msgb *msg)
|
||||
subscr_name(gsms->sender), sms_mti, sms_vpf, gsms->msg_ref,
|
||||
gsms->protocol_id, gsms->data_coding_scheme, gsms->dest_addr,
|
||||
gsms->user_data_len,
|
||||
sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
|
||||
sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text :
|
||||
hexdump(gsms->user_data, gsms->user_data_len));
|
||||
|
||||
gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp);
|
||||
@@ -610,10 +602,10 @@ static int gsm340_rx_tpdu(struct msgb *msg)
|
||||
dispatch_signal(SS_SMS, 0, gsms);
|
||||
|
||||
/* determine gsms->receiver based on dialled number */
|
||||
gsms->receiver = subscr_get_by_extension(bts->network, gsms->dest_addr);
|
||||
gsms->receiver = subscr_get_by_extension(conn->bts->network, gsms->dest_addr);
|
||||
if (!gsms->receiver) {
|
||||
rc = 1; /* cause 1: unknown subscriber */
|
||||
counter_inc(bts->network->stats.sms.no_receiver);
|
||||
counter_inc(conn->bts->network->stats.sms.no_receiver);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -683,11 +675,11 @@ static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans,
|
||||
GSM411_RP_CAUSE_INV_MAND_INF);
|
||||
return -EIO;
|
||||
}
|
||||
msg->smsh = tpdu;
|
||||
msg->l4h = tpdu;
|
||||
|
||||
DEBUGP(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len));
|
||||
|
||||
rc = gsm340_rx_tpdu(msg);
|
||||
rc = gsm340_rx_tpdu(trans->conn, msg);
|
||||
if (rc == 0)
|
||||
return gsm411_send_rp_ack(trans, rph->msg_ref);
|
||||
else if (rc > 0)
|
||||
@@ -753,16 +745,19 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
|
||||
trans->sms.sms = NULL;
|
||||
|
||||
/* check for more messages for this subscriber */
|
||||
sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
|
||||
assert(msg->lchan->conn.subscr == trans->subscr);
|
||||
|
||||
sms = db_sms_get_unsent_for_subscr(trans->subscr);
|
||||
if (sms)
|
||||
gsm411_send_sms_lchan(msg->lchan, sms);
|
||||
gsm411_send_sms_lchan(trans->conn, sms);
|
||||
|
||||
/* free the transaction here */
|
||||
trans_free(trans);
|
||||
|
||||
/* release channel if done */
|
||||
#warning "BROKEN. The SAPI will be released automatically by the BSC"
|
||||
if (!sms)
|
||||
rsl_release_request(msg->lchan, trans->sms.link_id);
|
||||
rsl_release_request(msg->lchan, trans->sms.link_id, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -770,7 +765,7 @@ static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans,
|
||||
static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
|
||||
struct gsm411_rp_hdr *rph)
|
||||
{
|
||||
struct gsm_network *net = trans->lchan->ts->trx->bts->network;
|
||||
struct gsm_network *net = trans->conn->bts->network;
|
||||
struct gsm_sms *sms = trans->sms.sms;
|
||||
u_int8_t cause_len = rph->data[0];
|
||||
u_int8_t cause = rph->data[1];
|
||||
@@ -780,7 +775,7 @@ static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans,
|
||||
* the cause and take action depending on it */
|
||||
|
||||
LOGP(DSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n",
|
||||
subscr_name(msg->lchan->subscr), cause_len, cause,
|
||||
subscr_name(trans->conn->subscr), cause_len, cause,
|
||||
get_value_string(rp_cause_strs, cause));
|
||||
|
||||
if (!trans->sms.is_mt) {
|
||||
@@ -833,11 +828,13 @@ static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans,
|
||||
dispatch_signal(SS_SMS, S_SMS_SMMA, trans->subscr);
|
||||
|
||||
/* check for more messages for this subscriber */
|
||||
sms = db_sms_get_unsent_for_subscr(msg->lchan->subscr);
|
||||
assert(msg->lchan->conn.subscr == trans->subscr);
|
||||
sms = db_sms_get_unsent_for_subscr(trans->subscr);
|
||||
if (sms)
|
||||
gsm411_send_sms_lchan(msg->lchan, sms);
|
||||
gsm411_send_sms_lchan(trans->conn, sms);
|
||||
else
|
||||
rsl_release_request(msg->lchan, trans->sms.link_id);
|
||||
rsl_release_request(msg->lchan, trans->sms.link_id, 0);
|
||||
#warning "BROKEN: The SAPI=3 will be released automatically by the BSC"
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -920,16 +917,16 @@ int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id)
|
||||
struct gsm_trans *trans;
|
||||
int rc = 0;
|
||||
|
||||
if (!lchan->subscr)
|
||||
if (!lchan->conn.subscr)
|
||||
return -EIO;
|
||||
/* FIXME: send some error message */
|
||||
|
||||
DEBUGP(DSMS, "trans_id=%x ", transaction_id);
|
||||
trans = trans_find_by_id(lchan->subscr, GSM48_PDISC_SMS,
|
||||
trans = trans_find_by_id(lchan->conn.subscr, GSM48_PDISC_SMS,
|
||||
transaction_id);
|
||||
if (!trans) {
|
||||
DEBUGPC(DSMS, "(new) ");
|
||||
trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
|
||||
trans = trans_alloc(lchan->conn.subscr, GSM48_PDISC_SMS,
|
||||
transaction_id, new_callref++);
|
||||
if (!trans) {
|
||||
DEBUGPC(DSMS, "No memory for trans\n");
|
||||
@@ -941,8 +938,8 @@ int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id)
|
||||
trans->sms.is_mt = 0;
|
||||
trans->sms.link_id = link_id;
|
||||
|
||||
trans->lchan = lchan;
|
||||
use_lchan(lchan);
|
||||
trans->conn = &lchan->conn;
|
||||
use_subscr_con(trans->conn);
|
||||
}
|
||||
|
||||
switch(msg_type) {
|
||||
@@ -961,7 +958,7 @@ int gsm0411_rcv_sms(struct msgb *msg, u_int8_t link_id)
|
||||
if (i == transaction_id)
|
||||
continue;
|
||||
|
||||
ptrans = trans_find_by_id(lchan->subscr,
|
||||
ptrans = trans_find_by_id(lchan->conn.subscr,
|
||||
GSM48_PDISC_SMS, i);
|
||||
if (!ptrans)
|
||||
continue;
|
||||
@@ -1041,7 +1038,7 @@ static u_int8_t tpdu_test[] = {
|
||||
/* Take a SMS in gsm_sms structure and send it through an already
|
||||
* existing lchan. We also assume that the caller ensured this lchan already
|
||||
* has a SAPI3 RLL connection! */
|
||||
int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
|
||||
int gsm411_send_sms_lchan(struct gsm_subscriber_connection *conn, struct gsm_sms *sms)
|
||||
{
|
||||
struct msgb *msg = gsm411_msgb_alloc();
|
||||
struct gsm_trans *trans;
|
||||
@@ -1050,18 +1047,16 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
|
||||
int transaction_id;
|
||||
int rc;
|
||||
|
||||
transaction_id = trans_assign_trans_id(lchan->subscr, GSM48_PDISC_SMS, 0);
|
||||
transaction_id = trans_assign_trans_id(conn->subscr, GSM48_PDISC_SMS, 0);
|
||||
if (transaction_id == -1) {
|
||||
LOGP(DSMS, LOGL_ERROR, "No available transaction ids\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
msg->lchan = lchan;
|
||||
|
||||
DEBUGP(DSMS, "send_sms_lchan()\n");
|
||||
|
||||
/* FIXME: allocate transaction with message reference */
|
||||
trans = trans_alloc(lchan->subscr, GSM48_PDISC_SMS,
|
||||
trans = trans_alloc(conn->subscr, GSM48_PDISC_SMS,
|
||||
transaction_id, new_callref++);
|
||||
if (!trans) {
|
||||
LOGP(DSMS, LOGL_ERROR, "No memory for trans\n");
|
||||
@@ -1074,8 +1069,8 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
|
||||
trans->sms.sms = sms;
|
||||
trans->sms.link_id = UM_SAPI_SMS; /* FIXME: main or SACCH ? */
|
||||
|
||||
trans->lchan = lchan;
|
||||
use_lchan(lchan);
|
||||
trans->conn = conn;
|
||||
use_subscr_con(trans->conn);
|
||||
|
||||
/* Hardcode SMSC Originating Address for now */
|
||||
data = (u_int8_t *)msgb_put(msg, 8);
|
||||
@@ -1112,7 +1107,7 @@ int gsm411_send_sms_lchan(struct gsm_lchan *lchan, struct gsm_sms *sms)
|
||||
|
||||
DEBUGP(DSMS, "TX: SMS DELIVER\n");
|
||||
|
||||
counter_inc(lchan->ts->trx->bts->network->stats.sms.delivered);
|
||||
counter_inc(conn->bts->network->stats.sms.delivered);
|
||||
|
||||
return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref);
|
||||
/* FIXME: enter 'wait for RP-ACK' state, start TR1N */
|
||||
@@ -1130,11 +1125,13 @@ static void rll_ind_cb(struct gsm_lchan *lchan, u_int8_t link_id,
|
||||
|
||||
switch (type) {
|
||||
case BSC_RLLR_IND_EST_CONF:
|
||||
gsm411_send_sms_lchan(lchan, sms);
|
||||
#warning "BROKEN: The BSC will establish this transparently"
|
||||
gsm411_send_sms_lchan(&lchan->conn, sms);
|
||||
break;
|
||||
case BSC_RLLR_IND_REL_IND:
|
||||
case BSC_RLLR_IND_ERR_IND:
|
||||
case BSC_RLLR_IND_TIMEOUT:
|
||||
#warning "BROKEN: We will need to handle SAPI n Reject"
|
||||
sms_free(sms);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ static int parse_process_uss_req(u_int8_t *uss_req_data, u_int8_t length,
|
||||
gsm_7bit_decode(req->text,
|
||||
&(uss_req_data[7]), num_chars);
|
||||
/* append null-terminator */
|
||||
req->text[num_chars+1] = 0;
|
||||
req->text[num_chars+1] = 0;
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
@@ -257,7 +257,6 @@ int gsm0480_send_ussd_response(const struct msgb *in_msg, const char *response_t
|
||||
if (((strlen(response_text) * 7) % 8) != 0)
|
||||
response_len += 1;
|
||||
|
||||
msg->bts_link = in_msg->bts_link;
|
||||
msg->lchan = in_msg->lchan;
|
||||
|
||||
/* First put the payload text into the message */
|
||||
@@ -304,7 +303,6 @@ int gsm0480_send_ussd_reject(const struct msgb *in_msg,
|
||||
struct msgb *msg = gsm48_msgb_alloc();
|
||||
struct gsm48_hdr *gh;
|
||||
|
||||
msg->bts_link = in_msg->bts_link;
|
||||
msg->lchan = in_msg->lchan;
|
||||
|
||||
/* First insert the problem code */
|
||||
|
||||
@@ -45,52 +45,41 @@ void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr,
|
||||
ts->e1_link.e1_ts_ss = e1_ts_ss;
|
||||
}
|
||||
|
||||
static const char *pchan_names[] = {
|
||||
[GSM_PCHAN_NONE] = "NONE",
|
||||
[GSM_PCHAN_CCCH] = "CCCH",
|
||||
[GSM_PCHAN_CCCH_SDCCH4] = "CCCH+SDCCH4",
|
||||
[GSM_PCHAN_TCH_F] = "TCH/F",
|
||||
[GSM_PCHAN_TCH_H] = "TCH/H",
|
||||
[GSM_PCHAN_SDCCH8_SACCH8C] = "SDCCH8",
|
||||
[GSM_PCHAN_PDCH] = "PDCH",
|
||||
[GSM_PCHAN_TCH_F_PDCH] = "TCH/F_PDCH",
|
||||
[GSM_PCHAN_UNKNOWN] = "UNKNOWN",
|
||||
static const struct value_string pchan_names[] = {
|
||||
{ GSM_PCHAN_NONE, "NONE" },
|
||||
{ GSM_PCHAN_CCCH, "CCCH" },
|
||||
{ GSM_PCHAN_CCCH_SDCCH4,"CCCH+SDCCH4" },
|
||||
{ GSM_PCHAN_TCH_F, "TCH/F" },
|
||||
{ GSM_PCHAN_TCH_H, "TCH/H" },
|
||||
{ GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8" },
|
||||
{ GSM_PCHAN_PDCH, "PDCH" },
|
||||
{ GSM_PCHAN_TCH_F_PDCH, "TCH/F_PDCH" },
|
||||
{ GSM_PCHAN_UNKNOWN, "UNKNOWN" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *gsm_pchan_name(enum gsm_phys_chan_config c)
|
||||
{
|
||||
if (c >= ARRAY_SIZE(pchan_names))
|
||||
return "INVALID";
|
||||
|
||||
return pchan_names[c];
|
||||
return get_value_string(pchan_names, c);
|
||||
}
|
||||
|
||||
enum gsm_phys_chan_config gsm_pchan_parse(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pchan_names); i++) {
|
||||
if (!strcasecmp(name, pchan_names[i]))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return get_string_value(pchan_names, name);
|
||||
}
|
||||
|
||||
static const char *lchan_names[] = {
|
||||
[GSM_LCHAN_NONE] = "NONE",
|
||||
[GSM_LCHAN_SDCCH] = "SDCCH",
|
||||
[GSM_LCHAN_TCH_F] = "TCH/F",
|
||||
[GSM_LCHAN_TCH_H] = "TCH/H",
|
||||
[GSM_LCHAN_UNKNOWN] = "UNKNOWN",
|
||||
static const struct value_string lchant_names[] = {
|
||||
{ GSM_LCHAN_NONE, "NONE" },
|
||||
{ GSM_LCHAN_SDCCH, "SDCCH" },
|
||||
{ GSM_LCHAN_TCH_F, "TCH/F" },
|
||||
{ GSM_LCHAN_TCH_H, "TCH/H" },
|
||||
{ GSM_LCHAN_UNKNOWN, "UNKNOWN" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *gsm_lchant_name(enum gsm_chan_t c)
|
||||
{
|
||||
if (c >= ARRAY_SIZE(lchan_names))
|
||||
return "INVALID";
|
||||
|
||||
return lchan_names[c];
|
||||
return get_value_string(lchant_names, c);
|
||||
}
|
||||
|
||||
static const struct value_string lchan_s_names[] = {
|
||||
@@ -99,7 +88,8 @@ static const struct value_string lchan_s_names[] = {
|
||||
{ LCHAN_S_ACTIVE, "ACTIVE" },
|
||||
{ LCHAN_S_INACTIVE, "INACTIVE" },
|
||||
{ LCHAN_S_REL_REQ, "RELEASE REQUESTED" },
|
||||
{ 0, NULL },
|
||||
{ LCHAN_S_REL_ERR, "RELEASE DUE ERROR" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *gsm_lchans_name(enum gsm_lchan_state s)
|
||||
@@ -107,20 +97,18 @@ const char *gsm_lchans_name(enum gsm_lchan_state s)
|
||||
return get_value_string(lchan_s_names, s);
|
||||
}
|
||||
|
||||
static const char *chreq_names[] = {
|
||||
[GSM_CHREQ_REASON_EMERG] = "EMERGENCY",
|
||||
[GSM_CHREQ_REASON_PAG] = "PAGING",
|
||||
[GSM_CHREQ_REASON_CALL] = "CALL",
|
||||
[GSM_CHREQ_REASON_LOCATION_UPD] = "LOCATION_UPDATE",
|
||||
[GSM_CHREQ_REASON_OTHER] = "OTHER",
|
||||
static const struct value_string chreq_names[] = {
|
||||
{ GSM_CHREQ_REASON_EMERG, "EMERGENCY" },
|
||||
{ GSM_CHREQ_REASON_PAG, "PAGING" },
|
||||
{ GSM_CHREQ_REASON_CALL, "CALL" },
|
||||
{ GSM_CHREQ_REASON_LOCATION_UPD,"LOCATION_UPDATE" },
|
||||
{ GSM_CHREQ_REASON_OTHER, "OTHER" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
const char *gsm_chreq_name(enum gsm_chreq_reason_t c)
|
||||
{
|
||||
if (c >= ARRAY_SIZE(chreq_names))
|
||||
return "INVALID";
|
||||
|
||||
return chreq_names[c];
|
||||
return get_value_string(chreq_names, c);
|
||||
}
|
||||
|
||||
static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type)
|
||||
@@ -234,6 +222,10 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type,
|
||||
}
|
||||
bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4;
|
||||
|
||||
bts->paging.free_chans_need = -1;
|
||||
bts->rach_b_thresh = -1;
|
||||
bts->rach_ldavg_slots = -1;
|
||||
|
||||
llist_add_tail(&bts->list, &net->bts_list);
|
||||
|
||||
return bts;
|
||||
@@ -293,9 +285,24 @@ struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_c
|
||||
net->stats.call.dialled = counter_alloc("net.call.dialled");
|
||||
net->stats.call.alerted = counter_alloc("net.call.alerted");
|
||||
net->stats.call.connected = counter_alloc("net.call.connected");
|
||||
net->stats.chan.rf_fail = counter_alloc("net.chan.rf_fail");
|
||||
net->stats.chan.rll_err = counter_alloc("net.chan.rll_err");
|
||||
net->stats.bts.oml_fail = counter_alloc("net.bts.oml_fail");
|
||||
net->stats.bts.rsl_fail = counter_alloc("net.bts.rsl_fail");
|
||||
|
||||
net->mncc_recv = mncc_recv;
|
||||
|
||||
gsm_net_update_ctype(net);
|
||||
|
||||
net->core_country_code = -1;
|
||||
net->core_network_code = -1;
|
||||
net->rtp_base_port = 4000;
|
||||
|
||||
net->msc_ip = talloc_strdup(net, "127.0.0.1");
|
||||
net->msc_port = 5000;
|
||||
net->ping_timeout = 20;
|
||||
net->pong_timeout = 5;
|
||||
|
||||
return net;
|
||||
}
|
||||
|
||||
@@ -377,27 +384,21 @@ char *gsm_lchan_name(struct gsm_lchan *lchan)
|
||||
return ts2str;
|
||||
}
|
||||
|
||||
static const char *bts_types[] = {
|
||||
[GSM_BTS_TYPE_UNKNOWN] = "unknown",
|
||||
[GSM_BTS_TYPE_BS11] = "bs11",
|
||||
[GSM_BTS_TYPE_NANOBTS] = "nanobts",
|
||||
static const struct value_string bts_types[] = {
|
||||
{ GSM_BTS_TYPE_UNKNOWN, "unknown" },
|
||||
{ GSM_BTS_TYPE_BS11, "bs11" },
|
||||
{ GSM_BTS_TYPE_NANOBTS, "nanobts" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
enum gsm_bts_type parse_btstype(const char *arg)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(bts_types); i++) {
|
||||
if (!strcmp(arg, bts_types[i]))
|
||||
return i;
|
||||
}
|
||||
return GSM_BTS_TYPE_BS11; /* Default: BS11 */
|
||||
return get_string_value(bts_types, arg);
|
||||
}
|
||||
|
||||
const char *btstype2str(enum gsm_bts_type type)
|
||||
{
|
||||
if (type > ARRAY_SIZE(bts_types))
|
||||
return "undefined";
|
||||
return bts_types[type];
|
||||
return get_value_string(bts_types, type);
|
||||
}
|
||||
|
||||
struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr)
|
||||
@@ -438,54 +439,21 @@ struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *gsm_auth_policy_names[] = {
|
||||
[GSM_AUTH_POLICY_CLOSED] = "closed",
|
||||
[GSM_AUTH_POLICY_ACCEPT_ALL] = "accept-all",
|
||||
[GSM_AUTH_POLICY_TOKEN] = "token",
|
||||
static const struct value_string auth_policy_names[] = {
|
||||
{ GSM_AUTH_POLICY_CLOSED, "closed" },
|
||||
{ GSM_AUTH_POLICY_ACCEPT_ALL, "accept-all" },
|
||||
{ GSM_AUTH_POLICY_TOKEN, "token" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
enum gsm_auth_policy gsm_auth_policy_parse(const char *arg)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(gsm_auth_policy_names); i++) {
|
||||
if (!strcmp(arg, gsm_auth_policy_names[i]))
|
||||
return i;
|
||||
}
|
||||
return GSM_AUTH_POLICY_CLOSED;
|
||||
return get_string_value(auth_policy_names, arg);
|
||||
}
|
||||
|
||||
const char *gsm_auth_policy_name(enum gsm_auth_policy policy)
|
||||
{
|
||||
if (policy > ARRAY_SIZE(gsm_auth_policy_names))
|
||||
return "undefined";
|
||||
return gsm_auth_policy_names[policy];
|
||||
}
|
||||
|
||||
/* this should not be here but in gsm_04_08... but that creates
|
||||
in turn a dependency nightmare (abis_nm depending on 04_08, ...) */
|
||||
static int gsm48_construct_ra(u_int8_t *buf, const struct gprs_ra_id *raid)
|
||||
{
|
||||
u_int16_t mcc = raid->mcc;
|
||||
u_int16_t mnc = raid->mnc;
|
||||
|
||||
buf[0] = ((mcc / 100) % 10) | (((mcc / 10) % 10) << 4);
|
||||
buf[1] = (mcc % 10);
|
||||
|
||||
/* I wonder who came up with the stupidity of encoding the MNC
|
||||
* differently depending on how many digits its decimal number has! */
|
||||
if (mnc < 100) {
|
||||
buf[1] |= 0xf0;
|
||||
buf[2] = ((mnc / 10) % 10) | ((mnc % 10) << 4);
|
||||
} else {
|
||||
buf[1] |= (mnc % 10) << 4;
|
||||
buf[2] = ((mnc / 100) % 10) | (((mcc / 10) % 10) << 4);
|
||||
}
|
||||
|
||||
*(u_int16_t *)(buf+3) = htons(raid->lac);
|
||||
|
||||
buf[5] = raid->rac;
|
||||
|
||||
return 6;
|
||||
return get_value_string(auth_policy_names, policy);
|
||||
}
|
||||
|
||||
void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts)
|
||||
@@ -505,28 +473,39 @@ int gsm48_ra_id_by_bts(u_int8_t *buf, struct gsm_bts *bts)
|
||||
return gsm48_construct_ra(buf, &raid);
|
||||
}
|
||||
|
||||
static const char *rrlp_mode_names[] = {
|
||||
[RRLP_MODE_NONE] = "none",
|
||||
[RRLP_MODE_MS_BASED] = "ms-based",
|
||||
[RRLP_MODE_MS_PREF] = "ms-preferred",
|
||||
[RRLP_MODE_ASS_PREF] = "ass-preferred",
|
||||
static const struct value_string rrlp_mode_names[] = {
|
||||
{ RRLP_MODE_NONE, "none" },
|
||||
{ RRLP_MODE_MS_BASED, "ms-based" },
|
||||
{ RRLP_MODE_MS_PREF, "ms-preferred" },
|
||||
{ RRLP_MODE_ASS_PREF, "ass-preferred" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
enum rrlp_mode rrlp_mode_parse(const char *arg)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(rrlp_mode_names); i++) {
|
||||
if (!strcmp(arg, rrlp_mode_names[i]))
|
||||
return i;
|
||||
}
|
||||
return RRLP_MODE_NONE;
|
||||
return get_string_value(rrlp_mode_names, arg);
|
||||
}
|
||||
|
||||
const char *rrlp_mode_name(enum rrlp_mode mode)
|
||||
{
|
||||
if (mode > ARRAY_SIZE(rrlp_mode_names))
|
||||
return "none";
|
||||
return rrlp_mode_names[mode];
|
||||
return get_value_string(rrlp_mode_names, mode);
|
||||
}
|
||||
|
||||
static const struct value_string bts_gprs_mode_names[] = {
|
||||
{ BTS_GPRS_NONE, "none" },
|
||||
{ BTS_GPRS_GPRS, "gprs" },
|
||||
{ BTS_GPRS_EGPRS, "egprs" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
enum bts_gprs_mode bts_gprs_mode_parse(const char *arg)
|
||||
{
|
||||
return get_string_value(bts_gprs_mode_names, arg);
|
||||
}
|
||||
|
||||
const char *bts_gprs_mode_name(enum bts_gprs_mode mode)
|
||||
{
|
||||
return get_value_string(bts_gprs_mode_names, mode);
|
||||
}
|
||||
|
||||
struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan)
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <openbsc/gsm_subscriber.h>
|
||||
#include <openbsc/paging.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/chan_alloc.h>
|
||||
|
||||
LLIST_HEAD(active_subscribers);
|
||||
void *tall_subscr_ctx;
|
||||
@@ -88,6 +89,7 @@ static int subscr_paging_cb(unsigned int hooknum, unsigned int event,
|
||||
request->cbfn(hooknum, event, msg, data, request->param);
|
||||
subscr->in_callback = 0;
|
||||
|
||||
subscr_put(request->subscr);
|
||||
talloc_free(request);
|
||||
return 0;
|
||||
}
|
||||
@@ -165,7 +167,7 @@ void subscr_get_channel(struct gsm_subscriber *subscr,
|
||||
}
|
||||
|
||||
memset(request, 0, sizeof(*request));
|
||||
request->subscr = subscr;
|
||||
request->subscr = subscr_get(subscr);
|
||||
request->channel_type = type;
|
||||
request->cbfn = cbfn;
|
||||
request->param = param;
|
||||
@@ -187,6 +189,7 @@ void subscr_get_channel(struct gsm_subscriber *subscr,
|
||||
|
||||
void subscr_put_channel(struct gsm_lchan *lchan)
|
||||
{
|
||||
struct gsm_subscriber_connection *conn = &lchan->conn;
|
||||
/*
|
||||
* FIXME: Continue with other requests now... by checking
|
||||
* the gsm_subscriber inside the gsm_lchan. Drop the ref count
|
||||
@@ -205,9 +208,28 @@ void subscr_put_channel(struct gsm_lchan *lchan)
|
||||
* will listen to the paging requests before we timeout
|
||||
*/
|
||||
|
||||
put_lchan(lchan);
|
||||
put_subscr_con(conn, 0);
|
||||
|
||||
if (lchan->subscr && !llist_empty(&lchan->subscr->requests))
|
||||
subscr_send_paging_request(lchan->subscr);
|
||||
if (lchan->conn.subscr && !llist_empty(&lchan->conn.subscr->requests))
|
||||
subscr_send_paging_request(lchan->conn.subscr);
|
||||
}
|
||||
|
||||
struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net,
|
||||
const char *imsi)
|
||||
{
|
||||
struct gsm_subscriber *subscr;
|
||||
|
||||
llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) {
|
||||
if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net)
|
||||
return subscr_get(subscr);
|
||||
}
|
||||
|
||||
subscr = subscr_alloc();
|
||||
if (!subscr)
|
||||
return NULL;
|
||||
|
||||
strcpy(subscr->imsi, imsi);
|
||||
subscr->net = net;
|
||||
return subscr;
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
|
||||
|
||||
counter_inc(bts->network->stats.handover.attempted);
|
||||
|
||||
new_lchan = lchan_alloc(bts, old_lchan->type);
|
||||
new_lchan = lchan_alloc(bts, old_lchan->type, 0);
|
||||
if (!new_lchan) {
|
||||
LOGP(DHO, LOGL_NOTICE, "No free channel\n");
|
||||
counter_inc(bts->network->stats.handover.no_channel);
|
||||
@@ -122,7 +122,7 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
|
||||
new_lchan->bs_power = old_lchan->bs_power;
|
||||
new_lchan->rsl_cmode = old_lchan->rsl_cmode;
|
||||
new_lchan->tch_mode = old_lchan->tch_mode;
|
||||
new_lchan->subscr = subscr_get(old_lchan->subscr);
|
||||
new_lchan->conn.subscr = subscr_get(old_lchan->conn.subscr);
|
||||
|
||||
/* FIXME: do we have a better idea of the timing advance? */
|
||||
rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, 0,
|
||||
@@ -134,6 +134,7 @@ int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ);
|
||||
llist_add(&ho->list, &bsc_handovers);
|
||||
/* we continue in the SS_LCHAN handler / ho_chan_activ_ack */
|
||||
|
||||
@@ -218,7 +219,7 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
|
||||
}
|
||||
|
||||
LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN "
|
||||
"%u->%u\n", subscr_name(ho->old_lchan->subscr),
|
||||
"%u->%u\n", subscr_name(ho->old_lchan->conn.subscr),
|
||||
ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr,
|
||||
ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn);
|
||||
|
||||
@@ -227,10 +228,9 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
|
||||
bsc_del_timer(&ho->T3103);
|
||||
|
||||
/* update lchan pointer of transaction */
|
||||
trans_lchan_change(ho->old_lchan, new_lchan);
|
||||
trans_lchan_change(&ho->old_lchan->conn, &new_lchan->conn);
|
||||
|
||||
ho->old_lchan->state = LCHAN_S_INACTIVE;
|
||||
lchan_auto_release(ho->old_lchan);
|
||||
rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE);
|
||||
|
||||
/* do something to re-route the actual speech frames ! */
|
||||
|
||||
@@ -243,6 +243,7 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
|
||||
/* GSM 04.08 HANDOVER FAIL has been received */
|
||||
static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
|
||||
{
|
||||
struct gsm_subscriber_connection *conn;
|
||||
struct gsm_network *net = old_lchan->ts->trx->bts->network;
|
||||
struct bsc_handover *ho;
|
||||
|
||||
@@ -256,7 +257,8 @@ static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
|
||||
|
||||
bsc_del_timer(&ho->T3103);
|
||||
llist_del(&ho->list);
|
||||
put_lchan(ho->new_lchan);
|
||||
conn = &ho->new_lchan->conn;
|
||||
put_subscr_con(conn, 0);
|
||||
talloc_free(ho);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/* OpenBSC Abis input driver for ip.access */
|
||||
|
||||
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2010 by Holger Hans Peter Freyther
|
||||
* (C) 2010 by On-Waves
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
@@ -44,6 +46,9 @@
|
||||
#include <openbsc/ipaccess.h>
|
||||
#include <osmocore/talloc.h>
|
||||
|
||||
#define PRIV_OML 1
|
||||
#define PRIV_RSL 2
|
||||
|
||||
/* data structure for one E1 interface with A-bis */
|
||||
struct ia_e1_handle {
|
||||
struct bsc_fd listen_fd;
|
||||
@@ -54,12 +59,12 @@ struct ia_e1_handle {
|
||||
static struct ia_e1_handle *e1h;
|
||||
|
||||
|
||||
#define TS1_ALLOC_SIZE 300
|
||||
#define TS1_ALLOC_SIZE 900
|
||||
|
||||
static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG };
|
||||
static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK };
|
||||
static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
|
||||
0x01, IPAC_IDTAG_UNIT,
|
||||
0x01, IPAC_IDTAG_UNIT,
|
||||
0x01, IPAC_IDTAG_MACADDR,
|
||||
0x01, IPAC_IDTAG_LOCATION1,
|
||||
0x01, IPAC_IDTAG_LOCATION2,
|
||||
@@ -230,29 +235,45 @@ static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
|
||||
return -EIO;
|
||||
}
|
||||
DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id);
|
||||
if (bfd->priv_nr == 1) {
|
||||
bts->oml_link = e1inp_sign_link_create(&line->ts[1-1],
|
||||
if (bfd->priv_nr == PRIV_OML) {
|
||||
/* drop any old oml connection */
|
||||
ipaccess_drop_oml(bts);
|
||||
bts->oml_link = e1inp_sign_link_create(&line->ts[PRIV_OML - 1],
|
||||
E1INP_SIGN_OML, bts->c0,
|
||||
bts->oml_tei, 0);
|
||||
} else if (bfd->priv_nr == 2) {
|
||||
} else if (bfd->priv_nr == PRIV_RSL) {
|
||||
struct e1inp_ts *e1i_ts;
|
||||
struct bsc_fd *newbfd;
|
||||
struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_id);
|
||||
|
||||
|
||||
/* drop any old rsl connection */
|
||||
ipaccess_drop_rsl(trx);
|
||||
|
||||
if (!bts->oml_link) {
|
||||
bsc_unregister_fd(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
talloc_free(bfd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bfd->data = line = bts->oml_link->ts->line;
|
||||
e1i_ts = &line->ts[2+trx_id - 1];
|
||||
e1i_ts = &line->ts[PRIV_RSL + trx_id - 1];
|
||||
newbfd = &e1i_ts->driver.ipaccess.fd;
|
||||
e1inp_ts_config(e1i_ts, line, E1INP_TS_TYPE_SIGN);
|
||||
|
||||
trx->rsl_link = e1inp_sign_link_create(e1i_ts,
|
||||
E1INP_SIGN_RSL, trx,
|
||||
trx->rsl_tei, 0);
|
||||
trx->rsl_link->ts->sign.delay = 10;
|
||||
|
||||
/* get rid of our old temporary bfd */
|
||||
memcpy(newbfd, bfd, sizeof(*newbfd));
|
||||
newbfd->priv_nr = 2+trx_id;
|
||||
newbfd->priv_nr = PRIV_RSL + trx_id;
|
||||
bsc_unregister_fd(bfd);
|
||||
bsc_register_fd(newbfd);
|
||||
bfd->fd = -1;
|
||||
talloc_free(bfd);
|
||||
bsc_register_fd(newbfd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -279,14 +300,14 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
|
||||
|
||||
/* first read our 3-byte header */
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
ret = recv(bfd->fd, msg->data, 3, 0);
|
||||
if (ret < 0) {
|
||||
if (errno != EAGAIN)
|
||||
LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno));
|
||||
ret = recv(bfd->fd, msg->data, sizeof(*hh), 0);
|
||||
if (ret == 0) {
|
||||
msgb_free(msg);
|
||||
*error = ret;
|
||||
return NULL;
|
||||
} else if (ret == 0) {
|
||||
} else if (ret != sizeof(*hh)) {
|
||||
if (errno != EAGAIN)
|
||||
LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno));
|
||||
msgb_free(msg);
|
||||
*error = ret;
|
||||
return NULL;
|
||||
@@ -297,9 +318,17 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
|
||||
/* then read te length as specified in header */
|
||||
msg->l2h = msg->data + sizeof(*hh);
|
||||
len = ntohs(hh->len);
|
||||
|
||||
if (len < 0 || TS1_ALLOC_SIZE < len + sizeof(*hh)) {
|
||||
LOGP(DINP, LOGL_ERROR, "Can not read this packet. %d avail\n", len);
|
||||
msgb_free(msg);
|
||||
*error = -EIO;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = recv(bfd->fd, msg->l2h, len, 0);
|
||||
if (ret < len) {
|
||||
LOGP(DINP, LOGL_ERROR, "short read!\n");
|
||||
LOGP(DINP, LOGL_ERROR, "short read! Got %d from %d\n", ret, len);
|
||||
msgb_free(msg);
|
||||
*error = -EIO;
|
||||
return NULL;
|
||||
@@ -309,6 +338,103 @@ struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
|
||||
return msg;
|
||||
}
|
||||
|
||||
int ipaccess_drop_oml(struct gsm_bts *bts)
|
||||
{
|
||||
struct gsm_bts_trx *trx;
|
||||
struct e1inp_ts *ts;
|
||||
struct e1inp_line *line;
|
||||
struct bsc_fd *bfd;
|
||||
|
||||
if (!bts || !bts->oml_link)
|
||||
return -1;
|
||||
|
||||
/* send OML down */
|
||||
ts = bts->oml_link->ts;
|
||||
line = ts->line;
|
||||
e1inp_event(ts, EVT_E1_TEI_DN, bts->oml_link->tei, bts->oml_link->sapi);
|
||||
|
||||
bfd = &ts->driver.ipaccess.fd;
|
||||
bsc_unregister_fd(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
|
||||
/* clean up OML and RSL */
|
||||
e1inp_sign_link_destroy(bts->oml_link);
|
||||
bts->oml_link = NULL;
|
||||
bts->ip_access.flags = 0;
|
||||
|
||||
/* drop all RSL connections too */
|
||||
llist_for_each_entry(trx, &bts->trx_list, list)
|
||||
ipaccess_drop_rsl(trx);
|
||||
|
||||
/* kill the E1 line now... as we have no one left to use it */
|
||||
talloc_free(line);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ipaccess_drop(struct e1inp_ts *ts, struct bsc_fd *bfd)
|
||||
{
|
||||
struct e1inp_sign_link *link;
|
||||
int bts_nr;
|
||||
|
||||
if (!ts) {
|
||||
/*
|
||||
* If we don't have a TS this means that this is a RSL
|
||||
* connection but we are not past the authentication
|
||||
* handling yet. So we can safely delete this bfd and
|
||||
* wait for a reconnect.
|
||||
*/
|
||||
bsc_unregister_fd(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
talloc_free(bfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* attempt to find a signalling link */
|
||||
if (ts->type == E1INP_TS_TYPE_SIGN) {
|
||||
llist_for_each_entry(link, &ts->sign.sign_links, list) {
|
||||
bts_nr = link->trx->bts->bts_nr;
|
||||
/* we have issues just reconnecting RLS so we drop OML */
|
||||
ipaccess_drop_oml(link->trx->bts);
|
||||
return bts_nr;
|
||||
}
|
||||
}
|
||||
|
||||
/* error case */
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to find a signalling link for ts: %p\n", ts);
|
||||
bsc_unregister_fd(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ipaccess_drop_rsl(struct gsm_bts_trx *trx)
|
||||
{
|
||||
struct bsc_fd *bfd;
|
||||
struct e1inp_ts *ts;
|
||||
|
||||
if (!trx || !trx->rsl_link)
|
||||
return -1;
|
||||
|
||||
/* send RSL down */
|
||||
ts = trx->rsl_link->ts;
|
||||
e1inp_event(ts, EVT_E1_TEI_DN, trx->rsl_link->tei, trx->rsl_link->sapi);
|
||||
|
||||
/* close the socket */
|
||||
bfd = &ts->driver.ipaccess.fd;
|
||||
bsc_unregister_fd(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
|
||||
/* destroy */
|
||||
e1inp_sign_link_destroy(trx->rsl_link);
|
||||
trx->rsl_link = NULL;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int handle_ts1_read(struct bsc_fd *bfd)
|
||||
{
|
||||
struct e1inp_line *line = bfd->data;
|
||||
@@ -322,18 +448,12 @@ static int handle_ts1_read(struct bsc_fd *bfd)
|
||||
msg = ipaccess_read_msg(bfd, &error);
|
||||
if (!msg) {
|
||||
if (error == 0) {
|
||||
link = e1inp_lookup_sign_link(e1i_ts, IPAC_PROTO_OML, 0);
|
||||
if (link) {
|
||||
link->trx->bts->ip_access.flags = 0;
|
||||
int ret = ipaccess_drop(e1i_ts, bfd);
|
||||
if (ret >= 0)
|
||||
LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n",
|
||||
link->trx->bts->nr);
|
||||
} else
|
||||
ret);
|
||||
else
|
||||
LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n");
|
||||
e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL);
|
||||
e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML);
|
||||
bsc_unregister_fd(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
@@ -343,13 +463,8 @@ static int handle_ts1_read(struct bsc_fd *bfd)
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
if (hh->proto == IPAC_PROTO_IPACCESS) {
|
||||
ret = ipaccess_rcvmsg(line, msg, bfd);
|
||||
if (ret < 0) {
|
||||
e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_RSL);
|
||||
e1inp_event(e1i_ts, EVT_E1_TEI_DN, 0, IPAC_PROTO_OML);
|
||||
bsc_unregister_fd(bfd);
|
||||
close(bfd->fd);
|
||||
bfd->fd = -1;
|
||||
}
|
||||
if (ret < 0)
|
||||
ipaccess_drop(e1i_ts, bfd);
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
@@ -456,7 +571,9 @@ static int handle_ts1_write(struct bsc_fd *bfd)
|
||||
/* set tx delay timer for next event */
|
||||
e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
|
||||
e1i_ts->sign.tx_timer.data = e1i_ts;
|
||||
bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 100000);
|
||||
|
||||
/* Reducing this might break the nanoBTS 900 init. */
|
||||
bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -490,6 +607,7 @@ static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
|
||||
struct e1inp_driver ipaccess_driver = {
|
||||
.name = "ip.access",
|
||||
.want_write = ts_want_write,
|
||||
.default_delay = 100000,
|
||||
};
|
||||
|
||||
/* callback of the OML listening filedescriptor */
|
||||
@@ -497,6 +615,7 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
|
||||
{
|
||||
int ret;
|
||||
int idx = 0;
|
||||
int i;
|
||||
struct e1inp_line *line;
|
||||
struct e1inp_ts *e1i_ts;
|
||||
struct bsc_fd *bfd;
|
||||
@@ -524,12 +643,16 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
|
||||
/* create virrtual E1 timeslots for signalling */
|
||||
e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
|
||||
|
||||
/* initialize the fds */
|
||||
for (i = 0; i < ARRAY_SIZE(line->ts); ++i)
|
||||
line->ts[i].driver.ipaccess.fd.fd = -1;
|
||||
|
||||
e1i_ts = &line->ts[idx];
|
||||
|
||||
bfd = &e1i_ts->driver.ipaccess.fd;
|
||||
bfd->fd = ret;
|
||||
bfd->data = line;
|
||||
bfd->priv_nr = 1;
|
||||
bfd->priv_nr = PRIV_OML;
|
||||
bfd->cb = ipaccess_fd_cb;
|
||||
bfd->when = BSC_FD_READ;
|
||||
ret = bsc_register_fd(bfd);
|
||||
@@ -571,7 +694,7 @@ static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
|
||||
return bfd->fd;
|
||||
}
|
||||
LOGP(DINP, LOGL_NOTICE, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr));
|
||||
bfd->priv_nr = 2;
|
||||
bfd->priv_nr = PRIV_RSL;
|
||||
bfd->cb = ipaccess_fd_cb;
|
||||
bfd->when = BSC_FD_READ;
|
||||
ret = bsc_register_fd(bfd);
|
||||
@@ -645,7 +768,7 @@ int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
|
||||
bfd->cb = ipaccess_fd_cb;
|
||||
bfd->when = BSC_FD_READ | BSC_FD_WRITE;
|
||||
bfd->data = line;
|
||||
bfd->priv_nr = 1;
|
||||
bfd->priv_nr = PRIV_OML;
|
||||
|
||||
if (bfd->fd < 0) {
|
||||
LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n");
|
||||
|
||||
@@ -235,7 +235,7 @@ static int handle_ts1_write(struct bsc_fd *bfd)
|
||||
/* set tx delay timer for next event */
|
||||
e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
|
||||
e1i_ts->sign.tx_timer.data = e1i_ts;
|
||||
bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 50000);
|
||||
bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -262,7 +262,7 @@ static int handle_tsX_write(struct bsc_fd *bfd)
|
||||
|
||||
ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0);
|
||||
if (ret < sizeof(*hh) + BCHAN_TX_GRAN)
|
||||
DEBUGP(DMIB, "send returns %d instead of %lu\n", ret,
|
||||
DEBUGP(DMIB, "send returns %d instead of %zu\n", ret,
|
||||
sizeof(*hh) + BCHAN_TX_GRAN);
|
||||
|
||||
return ret;
|
||||
@@ -375,6 +375,7 @@ static int activate_bchan(struct e1inp_line *line, int ts, int act)
|
||||
struct e1inp_driver misdn_driver = {
|
||||
.name = "mISDNuser",
|
||||
.want_write = ts_want_write,
|
||||
.default_delay = 50000,
|
||||
};
|
||||
|
||||
static int mi_e1_setup(struct e1inp_line *line, int release_l2)
|
||||
|
||||
@@ -58,6 +58,8 @@ static char *software = NULL;
|
||||
static int sw_load_state = 0;
|
||||
static int oml_state = 0;
|
||||
static int dump_files = 0;
|
||||
static char *firmware_analysis = NULL;
|
||||
static int trx_nr = 0;
|
||||
|
||||
struct sw_load {
|
||||
u_int8_t file_id[255];
|
||||
@@ -315,7 +317,8 @@ static void bootstrap_om(struct gsm_bts *bts)
|
||||
memcpy(buf+3, unit_id, len);
|
||||
buf[3+len] = 0;
|
||||
printf("setting Unit ID to '%s'\n", unit_id);
|
||||
abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len+1);
|
||||
abis_nm_ipaccess_set_nvattr(gsm_bts_trx_by_nr(bts, trx_nr),
|
||||
buf, 3+len+1);
|
||||
}
|
||||
if (prim_oml_ip) {
|
||||
struct in_addr ia;
|
||||
@@ -353,7 +356,8 @@ static void bootstrap_om(struct gsm_bts *bts)
|
||||
*cur++ = nv_mask >> 8;
|
||||
printf("setting NV Flags/Mask to 0x%04x/0x%04x\n",
|
||||
nv_flags, nv_mask);
|
||||
abis_nm_ipaccess_set_nvattr(bts->c0, buf, 3+len);
|
||||
abis_nm_ipaccess_set_nvattr(gsm_bts_trx_by_nr(bts, trx_nr),
|
||||
buf, 3+len);
|
||||
}
|
||||
|
||||
if (restart && !prim_oml_ip && !software) {
|
||||
@@ -604,6 +608,7 @@ static void print_help(void)
|
||||
printf(" -d --software firmware\n");
|
||||
printf(" -f --firmware firmware Provide firmware information\n");
|
||||
printf(" -w --write-firmware. This will dump the firmware parts to the filesystem. Use with -f.\n");
|
||||
printf(" -t --trx NR. The TRX to use for the Unit ID and NVRAM attributes.\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@@ -611,14 +616,14 @@ int main(int argc, char **argv)
|
||||
struct gsm_bts *bts;
|
||||
struct sockaddr_in sin;
|
||||
int rc, option_index = 0, stream_id = 0xff;
|
||||
struct debug_target *stderr_target;
|
||||
struct log_target *stderr_target;
|
||||
|
||||
debug_init();
|
||||
stderr_target = debug_target_create_stderr();
|
||||
debug_add_target(stderr_target);
|
||||
debug_set_all_filter(stderr_target, 1);
|
||||
debug_set_log_level(stderr_target, 0);
|
||||
debug_parse_category_mask(stderr_target, "DNM,0");
|
||||
log_init(&log_info);
|
||||
stderr_target = log_target_create_stderr();
|
||||
log_add_target(stderr_target);
|
||||
log_set_all_filter(stderr_target, 1);
|
||||
log_set_log_level(stderr_target, 0);
|
||||
log_parse_category_mask(stderr_target, "DNM,0");
|
||||
bts_model_nanobts_init();
|
||||
|
||||
printf("ipaccess-config (C) 2009 by Harald Welte\n");
|
||||
@@ -638,9 +643,11 @@ int main(int argc, char **argv)
|
||||
{ "software", 1, 0, 'd' },
|
||||
{ "firmware", 1, 0, 'f' },
|
||||
{ "write-firmware", 0, 0, 'w' },
|
||||
{ "trx", 1, 0, 't' },
|
||||
{ 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:w", long_options,
|
||||
c = getopt_long(argc, argv, "u:o:rn:l:hs:d:f:wt:", long_options,
|
||||
&option_index);
|
||||
|
||||
if (c == -1)
|
||||
@@ -677,11 +684,14 @@ int main(int argc, char **argv)
|
||||
exit(0);
|
||||
break;
|
||||
case 'f':
|
||||
analyze_firmware(optarg);
|
||||
exit(0);
|
||||
firmware_analysis = optarg;
|
||||
break;
|
||||
case 'w':
|
||||
dump_files = 1;
|
||||
break;
|
||||
case 't':
|
||||
trx_nr = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
print_usage();
|
||||
print_help();
|
||||
@@ -689,8 +699,13 @@ int main(int argc, char **argv)
|
||||
}
|
||||
};
|
||||
|
||||
if (firmware_analysis)
|
||||
analyze_firmware(firmware_analysis);
|
||||
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n");
|
||||
/* only warn if we have not done anything else */
|
||||
if (!firmware_analysis)
|
||||
fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
@@ -718,6 +733,8 @@ int main(int argc, char **argv)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bts->oml_link->ts->sign.delay = 10;
|
||||
bts->c0->rsl_link->ts->sign.delay = 10;
|
||||
while (1) {
|
||||
rc = bsc_select_main(0);
|
||||
if (rc < 0)
|
||||
|
||||
@@ -71,7 +71,7 @@ static int udp_sock(const char *ifname)
|
||||
rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
#endif
|
||||
#endif
|
||||
return fd;
|
||||
|
||||
err:
|
||||
@@ -79,7 +79,7 @@ err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00,
|
||||
const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00,
|
||||
IPAC_MSGT_ID_GET,
|
||||
0x01, IPAC_IDTAG_MACADDR,
|
||||
0x01, IPAC_IDTAG_IPADDR,
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#include <openbsc/ipaccess.h>
|
||||
#include <osmocore/talloc.h>
|
||||
|
||||
static struct debug_target *stderr_target;
|
||||
static struct log_target *stderr_target;
|
||||
|
||||
/* one instance of an ip.access protocol proxy */
|
||||
struct ipa_proxy {
|
||||
@@ -265,10 +265,10 @@ static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int li
|
||||
struct ipa_bts_conn *ipbc, u_int8_t trx_id)
|
||||
{
|
||||
if (ipbc)
|
||||
debugp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id,
|
||||
logp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id,
|
||||
ipbc->unit_id.bts_id, trx_id);
|
||||
else
|
||||
debugp2(ss, lvl, file, line, 0, "unknown ");
|
||||
logp2(ss, lvl, file, line, 0, "unknown ");
|
||||
}
|
||||
|
||||
/* UDP socket handling */
|
||||
@@ -550,6 +550,7 @@ static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg,
|
||||
}
|
||||
|
||||
/* lookup BTS, create sign_link, ... */
|
||||
site_id = bts_id = trx_id = 0;
|
||||
parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT),
|
||||
&site_id, &bts_id, &trx_id);
|
||||
ipbc = find_bts_by_unitid(ipp, site_id, bts_id);
|
||||
@@ -946,7 +947,7 @@ static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
|
||||
perror("accept");
|
||||
return ret;
|
||||
}
|
||||
DEBUGP(DINP, "accept()ed new %s link from %s\n",
|
||||
DEBUGP(DINP, "accept()ed new %s link from %s\n",
|
||||
(listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL",
|
||||
inet_ntoa(sa.sin_addr));
|
||||
|
||||
@@ -1108,11 +1109,11 @@ int main(int argc, char **argv)
|
||||
|
||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy");
|
||||
|
||||
debug_init();
|
||||
stderr_target = debug_target_create_stderr();
|
||||
debug_add_target(stderr_target);
|
||||
debug_set_all_filter(stderr_target, 1);
|
||||
debug_parse_category_mask(stderr_target, "DINP:DMI");
|
||||
log_init(&log_info);
|
||||
stderr_target = log_target_create_stderr();
|
||||
log_add_target(stderr_target);
|
||||
log_set_all_filter(stderr_target, 1);
|
||||
log_parse_category_mask(stderr_target, "DINP:DMI");
|
||||
|
||||
rc = ipaccess_proxy_setup();
|
||||
if (rc < 0)
|
||||
|
||||
@@ -38,7 +38,13 @@
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <osmocore/select.h>
|
||||
#include <openbsc/mgcp.h>
|
||||
#include <openbsc/mgcp_internal.h>
|
||||
#include <openbsc/telnet_interface.h>
|
||||
#include <openbsc/vty.h>
|
||||
|
||||
#include <vty/command.h>
|
||||
|
||||
#include "../../bscconfig.h"
|
||||
|
||||
/* this is here for the vty... it will never be called */
|
||||
void subscr_put() { abort(); }
|
||||
@@ -49,8 +55,17 @@ void subscr_put() { abort(); }
|
||||
#warning "Make use of the rtp proxy code"
|
||||
|
||||
static struct bsc_fd bfd;
|
||||
static int first_request = 1;
|
||||
static struct mgcp_config *cfg;
|
||||
static int reset_endpoints = 0;
|
||||
|
||||
const char *openbsc_version = "OpenBSC MGCP " PACKAGE_VERSION;
|
||||
const char *openbsc_copyright =
|
||||
"Copyright (C) 2009-2010 Holger Freyther and On-Waves\n"
|
||||
"Contributions by Daniel Willmann, Jan Lübbe,Stefan Schmidt\n"
|
||||
"Dieter Spaar, Andreas Eversberg, Harald Welte\n\n"
|
||||
"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
|
||||
"This is free software: you are free to change and redistribute it.\n"
|
||||
"There is NO WARRANTY, to the extent permitted by law.\n";
|
||||
|
||||
static char *config_file = "mgcp.cfg";
|
||||
|
||||
@@ -64,6 +79,12 @@ static void print_help()
|
||||
printf(" -c --config-file filename The config file to use.\n");
|
||||
}
|
||||
|
||||
static void print_mgcp_version()
|
||||
{
|
||||
printf("%s\n\n", openbsc_version);
|
||||
printf("%s", openbsc_copyright);
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char** argv)
|
||||
{
|
||||
while (1) {
|
||||
@@ -71,10 +92,11 @@ static void handle_options(int argc, char** argv)
|
||||
static struct option long_options[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"config-file", 1, 0, 'c'},
|
||||
{"version", 0, 0, 'V'},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hc:", long_options, &option_index);
|
||||
c = getopt_long(argc, argv, "hc:V", long_options, &option_index);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
@@ -87,6 +109,10 @@ static void handle_options(int argc, char** argv)
|
||||
case 'c':
|
||||
config_file = talloc_strdup(tall_bsc_ctx, optarg);
|
||||
break;
|
||||
case 'V':
|
||||
print_mgcp_version();
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
/* ignore */
|
||||
break;
|
||||
@@ -94,12 +120,30 @@ static void handle_options(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
|
||||
/* simply remember this */
|
||||
static int mgcp_rsip_cb(struct mgcp_config *cfg)
|
||||
{
|
||||
reset_endpoints = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mgcp_change_cb(struct mgcp_config *cfg, int endpoint, int state, int local_rtp)
|
||||
{
|
||||
if (state != MGCP_ENDP_MDCX)
|
||||
return 0;
|
||||
|
||||
mgcp_send_dummy(&cfg->endpoints[endpoint]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_call_agent(struct bsc_fd *fd, unsigned int what)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t slen = sizeof(addr);
|
||||
struct msgb *msg;
|
||||
struct msgb *resp;
|
||||
int i;
|
||||
|
||||
msg = (struct msgb *) fd->data;
|
||||
|
||||
@@ -115,18 +159,6 @@ static int read_call_agent(struct bsc_fd *fd, unsigned int what)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (first_request) {
|
||||
first_request = 0;
|
||||
resp = mgcp_create_rsip();
|
||||
|
||||
if (resp) {
|
||||
sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0,
|
||||
(struct sockaddr *) &addr, sizeof(addr));
|
||||
msgb_free(resp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handle message now */
|
||||
msg->l2h = msgb_put(msg, rc);
|
||||
resp = mgcp_handle_message(cfg, msg);
|
||||
@@ -136,6 +168,16 @@ static int read_call_agent(struct bsc_fd *fd, unsigned int what)
|
||||
sendto(bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr));
|
||||
msgb_free(resp);
|
||||
}
|
||||
|
||||
if (reset_endpoints) {
|
||||
LOGP(DMGCP, LOGL_NOTICE, "Asked to reset endpoints.\n");
|
||||
reset_endpoints = 0;
|
||||
|
||||
/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
|
||||
for (i = 1; i < cfg->number_endpoints; ++i)
|
||||
mgcp_free_endp(&cfg->endpoints[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -145,14 +187,14 @@ int main(int argc, char** argv)
|
||||
struct gsm_network dummy_network;
|
||||
struct sockaddr_in addr;
|
||||
int on = 1, rc;
|
||||
struct debug_target *stderr_target;
|
||||
struct log_target *stderr_target;
|
||||
|
||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
|
||||
|
||||
debug_init();
|
||||
stderr_target = debug_target_create_stderr();
|
||||
debug_add_target(stderr_target);
|
||||
debug_set_all_filter(stderr_target, 1);
|
||||
log_init(&log_info);
|
||||
stderr_target = log_target_create_stderr();
|
||||
log_add_target(stderr_target);
|
||||
log_set_all_filter(stderr_target, 1);
|
||||
|
||||
cfg = mgcp_config_alloc();
|
||||
if (!cfg)
|
||||
@@ -165,6 +207,9 @@ int main(int argc, char** argv)
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* set some callbacks */
|
||||
cfg->reset_cb = mgcp_rsip_cb;
|
||||
cfg->change_cb = mgcp_change_cb;
|
||||
|
||||
/* we need to bind a socket */
|
||||
if (rc == 0) {
|
||||
@@ -196,11 +241,11 @@ int main(int argc, char** argv)
|
||||
|
||||
|
||||
if (bsc_register_fd(&bfd) != 0) {
|
||||
DEBUGP(DMGCP, "Failed to register the fd\n");
|
||||
LOGP(DMGCP, LOGL_FATAL, "Failed to register the fd\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUGP(DMGCP, "Configured for MGCP.\n");
|
||||
LOGP(DMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
|
||||
}
|
||||
|
||||
/* initialisation */
|
||||
@@ -214,3 +259,15 @@ int main(int argc, char** argv)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct gsm_network;
|
||||
int bsc_vty_init(struct gsm_network *dummy)
|
||||
{
|
||||
cmd_init(1);
|
||||
vty_init();
|
||||
|
||||
openbsc_vty_add_cmds();
|
||||
mgcp_vty_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocore/msgb.h>
|
||||
#include <osmocore/talloc.h>
|
||||
#include <osmocore/select.h>
|
||||
|
||||
#include <openbsc/debug.h>
|
||||
@@ -72,6 +72,8 @@ enum {
|
||||
PROTO_RTCP,
|
||||
};
|
||||
|
||||
#define DUMMY_LOAD 0x23
|
||||
|
||||
|
||||
static int udp_send(int fd, struct in_addr *addr, int port, char *buf, int len)
|
||||
{
|
||||
@@ -83,6 +85,14 @@ static int udp_send(int fd, struct in_addr *addr, int port, char *buf, int len)
|
||||
return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out));
|
||||
}
|
||||
|
||||
int mgcp_send_dummy(struct mgcp_endpoint *endp)
|
||||
{
|
||||
static char buf[] = { DUMMY_LOAD };
|
||||
|
||||
return udp_send(endp->local_rtp.fd, &endp->remote,
|
||||
endp->net_rtp, buf, 1);
|
||||
}
|
||||
|
||||
static void patch_payload(int payload, char *data, int len)
|
||||
{
|
||||
struct rtp_hdr *rtp_hdr;
|
||||
@@ -90,6 +100,9 @@ static void patch_payload(int payload, char *data, int len)
|
||||
if (len < sizeof(*rtp_hdr))
|
||||
return;
|
||||
|
||||
if (payload < 0)
|
||||
return;
|
||||
|
||||
rtp_hdr = (struct rtp_hdr *) data;
|
||||
rtp_hdr->payload_type = payload;
|
||||
}
|
||||
@@ -119,14 +132,14 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
|
||||
rc = recvfrom(fd->fd, &buf, sizeof(buf), 0,
|
||||
(struct sockaddr *) &addr, &slen);
|
||||
if (rc < 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x errno: %d/%s\n",
|
||||
ENDPOINT_NUMBER(endp), errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* do not forward aynthing... maybe there is a packet from the bts */
|
||||
if (endp->ci == CI_UNUSED) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Unknown message on endpoint: 0x%x\n", ENDPOINT_NUMBER(endp));
|
||||
LOGP(DMGCP, LOGL_DEBUG, "Unknown message on endpoint: 0x%x\n", ENDPOINT_NUMBER(endp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -146,7 +159,9 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
|
||||
/* We have no idea who called us, maybe it is the BTS. */
|
||||
if (dest == DEST_NETWORK && (endp->bts_rtp == 0 || cfg->forward_ip)) {
|
||||
/* it was the BTS... */
|
||||
if (!cfg->bts_ip || memcmp(&addr.sin_addr, &cfg->bts_in, sizeof(cfg->bts_in)) == 0) {
|
||||
if (!cfg->bts_ip
|
||||
|| memcmp(&addr.sin_addr, &cfg->bts_in, sizeof(cfg->bts_in)) == 0
|
||||
|| memcmp(&addr.sin_addr, &endp->bts, sizeof(endp->bts)) == 0) {
|
||||
if (fd == &endp->local_rtp) {
|
||||
endp->bts_rtp = addr.sin_port;
|
||||
} else {
|
||||
@@ -154,11 +169,26 @@ static int rtp_data_cb(struct bsc_fd *fd, unsigned int what)
|
||||
}
|
||||
|
||||
endp->bts = addr.sin_addr;
|
||||
LOGP(DMGCP, LOGL_NOTICE, "Found BTS for endpoint: 0x%x on port: %d/%d\n",
|
||||
ENDPOINT_NUMBER(endp), ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp));
|
||||
LOGP(DMGCP, LOGL_NOTICE, "Found BTS for endpoint: 0x%x on port: %d/%d of %s\n",
|
||||
ENDPOINT_NUMBER(endp), ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp),
|
||||
inet_ntoa(addr.sin_addr));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* throw away dummy message */
|
||||
if (rc == 1 && buf[0] == DUMMY_LOAD) {
|
||||
LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy on 0x%x\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* do this before the loop handling */
|
||||
if (dest == DEST_NETWORK)
|
||||
++endp->in_bts;
|
||||
else
|
||||
++endp->in_remote;
|
||||
|
||||
/* dispatch */
|
||||
if (cfg->audio_loop)
|
||||
dest = !dest;
|
||||
|
||||
@@ -80,11 +80,6 @@ enum mgcp_connection_mode {
|
||||
}
|
||||
|
||||
|
||||
struct mgcp_msg_ptr {
|
||||
unsigned int start;
|
||||
unsigned int length;
|
||||
};
|
||||
|
||||
struct mgcp_request {
|
||||
char *name;
|
||||
struct msgb *(*handle_request) (struct mgcp_config *cfg, struct msgb *msg);
|
||||
@@ -98,6 +93,7 @@ static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *
|
||||
static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg);
|
||||
static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg);
|
||||
static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg);
|
||||
static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg);
|
||||
|
||||
static int generate_call_id(struct mgcp_config *cfg)
|
||||
{
|
||||
@@ -119,12 +115,6 @@ static int generate_call_id(struct mgcp_config *cfg)
|
||||
return cfg->last_call_id;
|
||||
}
|
||||
|
||||
/* FIXIME/TODO: need to have a list of pending transactions and check that */
|
||||
static unsigned int generate_transaction_id()
|
||||
{
|
||||
return abs(rand());
|
||||
}
|
||||
|
||||
/*
|
||||
* array of function pointers for handling various
|
||||
* messages. In the future this might be binary sorted
|
||||
@@ -135,6 +125,9 @@ static const struct mgcp_request mgcp_requests [] = {
|
||||
MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection")
|
||||
MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection")
|
||||
MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection")
|
||||
|
||||
/* SPEC extension */
|
||||
MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress")
|
||||
};
|
||||
|
||||
static struct msgb *mgcp_msgb_alloc(void)
|
||||
@@ -194,23 +187,6 @@ static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp,
|
||||
return mgcp_create_response_with_data(200, msg, trans_id, sdp_record);
|
||||
}
|
||||
|
||||
/* send a static record */
|
||||
struct msgb *mgcp_create_rsip(void)
|
||||
{
|
||||
struct msgb *msg;
|
||||
int len;
|
||||
|
||||
msg = mgcp_msgb_alloc();
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
len = snprintf((char *) msg->data, 2048,
|
||||
"RSIP %u *@mgw MGCP 1.0\n"
|
||||
"RM: restart\n", generate_transaction_id());
|
||||
msg->l2h = msgb_put(msg, len);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle incoming messages:
|
||||
* - this can be a command (four letters, space, transaction id)
|
||||
@@ -221,25 +197,25 @@ struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg)
|
||||
int code;
|
||||
struct msgb *resp = NULL;
|
||||
|
||||
if (msg->len < 4) {
|
||||
if (msgb_l2len(msg) < 4) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "mgs too short: %d\n", msg->len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* attempt to treat it as a response */
|
||||
if (sscanf((const char *)&msg->data[0], "%3d %*s", &code) == 1) {
|
||||
if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) {
|
||||
LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code);
|
||||
} else {
|
||||
int i, handled = 0;
|
||||
msg->l3h = &msg->l2h[4];
|
||||
for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i)
|
||||
if (strncmp(mgcp_requests[i].name, (const char *) &msg->data[0], 4) == 0) {
|
||||
if (strncmp(mgcp_requests[i].name, (const char *) &msg->l2h[0], 4) == 0) {
|
||||
handled = 1;
|
||||
resp = mgcp_requests[i].handle_request(cfg, msg);
|
||||
break;
|
||||
}
|
||||
if (!handled) {
|
||||
LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->data[0]);
|
||||
LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,9 +272,9 @@ static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *
|
||||
return &cfg->endpoints[gw];
|
||||
}
|
||||
|
||||
static int analyze_header(struct mgcp_config *cfg, struct msgb *msg,
|
||||
struct mgcp_msg_ptr *ptr, int size,
|
||||
const char **transaction_id, struct mgcp_endpoint **endp)
|
||||
int mgcp_analyze_header(struct mgcp_config *cfg, struct msgb *msg,
|
||||
struct mgcp_msg_ptr *ptr, int size,
|
||||
const char **transaction_id, struct mgcp_endpoint **endp)
|
||||
{
|
||||
int found;
|
||||
|
||||
@@ -334,8 +310,11 @@ static int analyze_header(struct mgcp_config *cfg, struct msgb *msg,
|
||||
}
|
||||
|
||||
*transaction_id = (const char *)&msg->l3h[ptr[0].start];
|
||||
*endp = find_endpoint(cfg, (const char *)&msg->l3h[ptr[1].start]);
|
||||
return *endp == NULL;
|
||||
if (endp) {
|
||||
*endp = find_endpoint(cfg, (const char *)&msg->l3h[ptr[1].start]);
|
||||
return *endp == NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verify_call_id(const struct mgcp_endpoint *endp,
|
||||
@@ -369,7 +348,7 @@ static struct msgb *handle_audit_endpoint(struct mgcp_config *cfg, struct msgb *
|
||||
const char *trans_id;
|
||||
struct mgcp_endpoint *endp;
|
||||
|
||||
found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
|
||||
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
|
||||
if (found != 0)
|
||||
response = 500;
|
||||
else
|
||||
@@ -402,13 +381,20 @@ static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg)
|
||||
int error_code = 500;
|
||||
int port;
|
||||
|
||||
found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
|
||||
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
|
||||
if (found != 0)
|
||||
return create_response(500, "CRCX", trans_id);
|
||||
|
||||
if (endp->ci != CI_UNUSED) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n", ENDPOINT_NUMBER(endp));
|
||||
return create_response(500, "CRCX", trans_id);
|
||||
if (cfg->force_realloc) {
|
||||
LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
mgcp_free_endp(endp);
|
||||
} else {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
return create_response(500, "CRCX", trans_id);
|
||||
}
|
||||
}
|
||||
|
||||
/* parse CallID C: and LocalParameters L: */
|
||||
@@ -500,8 +486,9 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
|
||||
const char *trans_id;
|
||||
struct mgcp_endpoint *endp;
|
||||
int error_code = 500;
|
||||
int silent = 0;
|
||||
|
||||
found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
|
||||
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
|
||||
if (found != 0)
|
||||
return create_response(error_code, "MDCX", trans_id);
|
||||
|
||||
@@ -532,6 +519,9 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
|
||||
goto error3;
|
||||
}
|
||||
break;
|
||||
case 'Z':
|
||||
silent = strcmp("noanswer", (const char *)&msg->l3h[line_start + 3]) == 0;
|
||||
break;
|
||||
case '\0':
|
||||
/* SDP file begins */
|
||||
break;
|
||||
@@ -577,6 +567,8 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
|
||||
case MGCP_POLICY_REJECT:
|
||||
LOGP(DMGCP, LOGL_NOTICE, "MDCX rejected by policy on 0x%x\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
if (silent)
|
||||
goto out_silent;
|
||||
return create_response(500, "MDCX", trans_id);
|
||||
break;
|
||||
case MGCP_POLICY_DEFER:
|
||||
@@ -594,6 +586,9 @@ static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg)
|
||||
ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), ntohs(endp->net_rtp));
|
||||
if (cfg->change_cb)
|
||||
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, endp->rtp_port);
|
||||
if (silent)
|
||||
goto out_silent;
|
||||
|
||||
return create_response_with_sdp(endp, "MDCX", trans_id);
|
||||
|
||||
error:
|
||||
@@ -604,6 +599,10 @@ error:
|
||||
|
||||
error3:
|
||||
return create_response(error_code, "MDCX", trans_id);
|
||||
|
||||
|
||||
out_silent:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
|
||||
@@ -613,8 +612,9 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
|
||||
const char *trans_id;
|
||||
struct mgcp_endpoint *endp;
|
||||
int error_code = 500;
|
||||
int silent = 0;
|
||||
|
||||
found = analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
|
||||
found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp);
|
||||
if (found != 0)
|
||||
return create_response(error_code, "DLCX", trans_id);
|
||||
|
||||
@@ -634,6 +634,9 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
|
||||
if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0)
|
||||
goto error3;
|
||||
break;
|
||||
case 'Z':
|
||||
silent = strcmp("noanswer", (const char *)&msg->l3h[line_start + 3]) == 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n",
|
||||
@@ -649,6 +652,8 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
|
||||
case MGCP_POLICY_REJECT:
|
||||
LOGP(DMGCP, LOGL_NOTICE, "DLCX rejected by policy on 0x%x\n",
|
||||
ENDPOINT_NUMBER(endp));
|
||||
if (silent)
|
||||
goto out_silent;
|
||||
return create_response(500, "DLCX", trans_id);
|
||||
break;
|
||||
case MGCP_POLICY_DEFER:
|
||||
@@ -662,10 +667,14 @@ static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg)
|
||||
}
|
||||
|
||||
/* free the connection */
|
||||
LOGP(DMGCP, LOGL_NOTICE, "Deleted endpoint on: 0x%x Server: %s:%u\n",
|
||||
ENDPOINT_NUMBER(endp), inet_ntoa(endp->remote), ntohs(endp->net_rtp));
|
||||
mgcp_free_endp(endp);
|
||||
if (cfg->change_cb)
|
||||
cfg->change_cb(cfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, endp->rtp_port);
|
||||
|
||||
if (silent)
|
||||
goto out_silent;
|
||||
return create_response(250, "DLCX", trans_id);
|
||||
|
||||
error:
|
||||
@@ -676,6 +685,16 @@ error:
|
||||
|
||||
error3:
|
||||
return create_response(error_code, "DLCX", trans_id);
|
||||
|
||||
out_silent:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg)
|
||||
{
|
||||
if (cfg->reset_cb)
|
||||
cfg->reset_cb(cfg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mgcp_config *mgcp_config_alloc(void)
|
||||
@@ -722,7 +741,7 @@ int mgcp_endpoints_allocate(struct mgcp_config *cfg)
|
||||
|
||||
void mgcp_free_endp(struct mgcp_endpoint *endp)
|
||||
{
|
||||
LOGP(DMGCP, LOGL_NOTICE, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
|
||||
LOGP(DMGCP, LOGL_DEBUG, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp));
|
||||
endp->ci= CI_UNUSED;
|
||||
|
||||
if (endp->callid) {
|
||||
@@ -732,7 +751,7 @@ void mgcp_free_endp(struct mgcp_endpoint *endp)
|
||||
|
||||
if (endp->local_options) {
|
||||
talloc_free(endp->local_options);
|
||||
endp->callid = NULL;
|
||||
endp->local_options = NULL;
|
||||
}
|
||||
|
||||
if (!endp->cfg->early_bind) {
|
||||
@@ -742,4 +761,7 @@ void mgcp_free_endp(struct mgcp_endpoint *endp)
|
||||
|
||||
endp->net_rtp = endp->net_rtcp = endp->bts_rtp = endp->bts_rtcp = 0;
|
||||
endp->net_payload_type = endp->bts_payload_type = -1;
|
||||
endp->in_bts = endp->in_remote = 0;
|
||||
memset(&endp->remote, 0, sizeof(endp->remote));
|
||||
memset(&endp->bts, 0, sizeof(endp->bts));
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include <vty/command.h>
|
||||
#include <vty/vty.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static struct mgcp_config *g_cfg = NULL;
|
||||
|
||||
/*
|
||||
@@ -48,21 +50,25 @@ static int config_write_mgcp(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "mgcp%s", VTY_NEWLINE);
|
||||
if (g_cfg->local_ip)
|
||||
vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE);
|
||||
if (g_cfg->bts_ip)
|
||||
vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE);
|
||||
if (g_cfg->bts_ip && strlen(g_cfg->bts_ip) != 0)
|
||||
vty_out(vty, " bts ip %s%s", g_cfg->bts_ip, VTY_NEWLINE);
|
||||
vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE);
|
||||
vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE);
|
||||
vty_out(vty, " bind early %u%s", !!g_cfg->early_bind, VTY_NEWLINE);
|
||||
vty_out(vty, " rtp base %u%s", g_cfg->rtp_base_port, VTY_NEWLINE);
|
||||
vty_out(vty, " sdp audio payload number %u%s", g_cfg->audio_payload, VTY_NEWLINE);
|
||||
vty_out(vty, " sdp audio payload name %s%s", g_cfg->audio_name, VTY_NEWLINE);
|
||||
if (g_cfg->audio_payload != -1)
|
||||
vty_out(vty, " sdp audio payload number %d%s", g_cfg->audio_payload, VTY_NEWLINE);
|
||||
if (g_cfg->audio_name)
|
||||
vty_out(vty, " sdp audio payload name %s%s", g_cfg->audio_name, VTY_NEWLINE);
|
||||
vty_out(vty, " loop %u%s", !!g_cfg->audio_loop, VTY_NEWLINE);
|
||||
vty_out(vty, " endpoints %u%s", g_cfg->number_endpoints, VTY_NEWLINE);
|
||||
vty_out(vty, " number endpoints %u%s", g_cfg->number_endpoints - 1, VTY_NEWLINE);
|
||||
if (g_cfg->forward_ip)
|
||||
vty_out(vty, " forward audio ip %s%s", g_cfg->forward_ip, VTY_NEWLINE);
|
||||
vty_out(vty, " forward audio ip %s%s", g_cfg->forward_ip, VTY_NEWLINE);
|
||||
if (g_cfg->forward_port != 0)
|
||||
vty_out(vty, " forward audio port %d%s", g_cfg->forward_port, VTY_NEWLINE);
|
||||
vty_out(vty, " forward audio port %d%s", g_cfg->forward_port, VTY_NEWLINE);
|
||||
if (g_cfg->call_agent_addr)
|
||||
vty_out(vty, " call agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
@@ -75,10 +81,12 @@ DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp",
|
||||
vty_out(vty, "MGCP is up and running with %u endpoints:%s", g_cfg->number_endpoints - 1, VTY_NEWLINE);
|
||||
for (i = 1; i < g_cfg->number_endpoints; ++i) {
|
||||
struct mgcp_endpoint *endp = &g_cfg->endpoints[i];
|
||||
vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u%s",
|
||||
vty_out(vty, " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s traffic received bts: %u remote: %u%s",
|
||||
i, endp->ci,
|
||||
ntohs(endp->net_rtp), ntohs(endp->net_rtcp),
|
||||
ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp), VTY_NEWLINE);
|
||||
ntohs(endp->bts_rtp), ntohs(endp->bts_rtcp),
|
||||
inet_ntoa(endp->bts), endp->in_bts, endp->in_remote,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
@@ -237,6 +245,17 @@ DEFUN(cfg_mgcp_forward_port,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_mgcp_agent_addr,
|
||||
cfg_mgcp_agent_addr_cmd,
|
||||
"call agent ip IP",
|
||||
"Set the address of the call agent.")
|
||||
{
|
||||
if (g_cfg->call_agent_addr)
|
||||
talloc_free(g_cfg->call_agent_addr);
|
||||
g_cfg->call_agent_addr = talloc_strdup(g_cfg, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int mgcp_vty_init(void)
|
||||
{
|
||||
install_element(VIEW_NODE, &show_mgcp_cmd);
|
||||
@@ -256,6 +275,7 @@ int mgcp_vty_init(void)
|
||||
install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_forward_ip_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_forward_port_cmd);
|
||||
install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -274,6 +294,11 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg)
|
||||
if (!g_cfg->bts_ip)
|
||||
fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n");
|
||||
|
||||
if (!g_cfg->source_addr) {
|
||||
fprintf(stderr, "You need to specify a bind address.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mgcp_endpoints_allocate(g_cfg) != 0) {
|
||||
fprintf(stderr, "Failed to allocate endpoints: %d. Quitting.\n", g_cfg->number_endpoints);
|
||||
return -1;
|
||||
@@ -327,13 +352,3 @@ int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg)
|
||||
return !!g_cfg->forward_ip;
|
||||
}
|
||||
|
||||
struct gsm_network;
|
||||
int bsc_vty_init(struct gsm_network *dummy)
|
||||
{
|
||||
cmd_init(1);
|
||||
vty_init();
|
||||
|
||||
mgcp_vty_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -332,16 +332,16 @@ static int mncc_rcv_tchf(struct gsm_call *call, int msg_type,
|
||||
remote_trans = trans_find_by_callref(call->net, call->remote_ref);
|
||||
|
||||
/* this shouldn't really happen */
|
||||
if (!remote_trans || !remote_trans->lchan) {
|
||||
if (!remote_trans || !remote_trans->conn) {
|
||||
LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* RTP socket of remote end has meanwhile died */
|
||||
if (!remote_trans->lchan->abis_ip.rtp_socket)
|
||||
if (!remote_trans->conn->lchan->abis_ip.rtp_socket)
|
||||
return -EIO;
|
||||
|
||||
return rtp_send_frame(remote_trans->lchan->abis_ip.rtp_socket, dfr);
|
||||
return rtp_send_frame(remote_trans->conn->lchan->abis_ip.rtp_socket, dfr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
216
openbsc/src/nat/bsc_filter.c
Normal file
216
openbsc/src/nat/bsc_filter.c
Normal file
@@ -0,0 +1,216 @@
|
||||
/* BSC Multiplexer/NAT */
|
||||
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openbsc/bsc_nat.h>
|
||||
#include <openbsc/bssap.h>
|
||||
#include <openbsc/ipaccess.h>
|
||||
#include <openbsc/debug.h>
|
||||
|
||||
#include <osmocore/talloc.h>
|
||||
|
||||
#include <sccp/sccp.h>
|
||||
|
||||
/*
|
||||
* The idea is to have a simple struct describing a IPA packet with
|
||||
* SCCP SSN and the GSM 08.08 payload and decide. We will both have
|
||||
* a white and a blacklist of packets we want to handle.
|
||||
*
|
||||
* TODO: Implement a "NOT" in the filter language.
|
||||
*/
|
||||
|
||||
#define ALLOW_ANY -1
|
||||
|
||||
#define FILTER_TO_BSC 1
|
||||
#define FILTER_TO_MSC 2
|
||||
#define FILTER_TO_BOTH 3
|
||||
|
||||
|
||||
struct bsc_pkt_filter {
|
||||
int ipa_proto;
|
||||
int dest_ssn;
|
||||
int bssap;
|
||||
int gsm;
|
||||
int filter_dir;
|
||||
};
|
||||
|
||||
static struct bsc_pkt_filter black_list[] = {
|
||||
/* filter reset messages to the MSC */
|
||||
{ IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET, FILTER_TO_MSC },
|
||||
|
||||
/* filter reset ack messages to the BSC */
|
||||
{ IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET_ACKNOWLEDGE, FILTER_TO_BSC },
|
||||
|
||||
/* filter ip access */
|
||||
{ IPAC_PROTO_IPACCESS, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_MSC },
|
||||
};
|
||||
|
||||
static struct bsc_pkt_filter white_list[] = {
|
||||
/* allow IPAC_PROTO_SCCP messages to both sides */
|
||||
{ IPAC_PROTO_SCCP, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH },
|
||||
|
||||
/* allow MGCP messages to both sides */
|
||||
{ NAT_IPAC_PROTO_MGCP, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH },
|
||||
};
|
||||
|
||||
struct bsc_nat_parsed* bsc_nat_parse(struct msgb *msg)
|
||||
{
|
||||
struct sccp_parse_result result;
|
||||
struct bsc_nat_parsed *parsed;
|
||||
struct ipaccess_head *hh;
|
||||
|
||||
/* quick fail */
|
||||
if (msg->len < 4)
|
||||
return NULL;
|
||||
|
||||
parsed = talloc_zero(msg, struct bsc_nat_parsed);
|
||||
if (!parsed)
|
||||
return NULL;
|
||||
|
||||
/* more init */
|
||||
parsed->ipa_proto = parsed->called_ssn = parsed->calling_ssn = -1;
|
||||
parsed->sccp_type = parsed->bssap = parsed->gsm_type = -1;
|
||||
|
||||
/* start parsing */
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
parsed->ipa_proto = hh->proto;
|
||||
|
||||
msg->l2h = &hh->data[0];
|
||||
|
||||
/* do a size check on the input */
|
||||
if (ntohs(hh->len) != msgb_l2len(msg)) {
|
||||
LOGP(DINP, LOGL_ERROR, "Wrong input length?\n");
|
||||
talloc_free(parsed);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* analyze sccp down here */
|
||||
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
|
||||
memset(&result, 0, sizeof(result));
|
||||
if (sccp_parse_header(msg, &result) != 0) {
|
||||
talloc_free(parsed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (msg->l3h && msgb_l3len(msg) < 3) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Not enough space or GSM payload\n");
|
||||
talloc_free(parsed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
parsed->sccp_type = sccp_determine_msg_type(msg);
|
||||
parsed->src_local_ref = result.source_local_reference;
|
||||
parsed->dest_local_ref = result.destination_local_reference;
|
||||
parsed->called_ssn = result.called.ssn;
|
||||
parsed->calling_ssn = result.calling.ssn;
|
||||
|
||||
/* in case of connection confirm we have no payload */
|
||||
if (msg->l3h) {
|
||||
parsed->bssap = msg->l3h[0];
|
||||
parsed->gsm_type = msg->l3h[2];
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
int bsc_nat_filter_ipa(int dir, struct msgb *msg, struct bsc_nat_parsed *parsed)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* go through the blacklist now */
|
||||
for (i = 0; i < ARRAY_SIZE(black_list); ++i) {
|
||||
/* ignore the rule? */
|
||||
if (black_list[i].filter_dir != FILTER_TO_BOTH
|
||||
&& black_list[i].filter_dir != dir)
|
||||
continue;
|
||||
|
||||
/* the proto is not blacklisted */
|
||||
if (black_list[i].ipa_proto != ALLOW_ANY
|
||||
&& black_list[i].ipa_proto != parsed->ipa_proto)
|
||||
continue;
|
||||
|
||||
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
|
||||
/* the SSN is not blacklisted */
|
||||
if (black_list[i].dest_ssn != ALLOW_ANY
|
||||
&& black_list[i].dest_ssn != parsed->called_ssn)
|
||||
continue;
|
||||
|
||||
/* bssap */
|
||||
if (black_list[i].bssap != ALLOW_ANY
|
||||
&& black_list[i].bssap != parsed->bssap)
|
||||
continue;
|
||||
|
||||
/* gsm */
|
||||
if (black_list[i].gsm != ALLOW_ANY
|
||||
&& black_list[i].gsm != parsed->gsm_type)
|
||||
continue;
|
||||
|
||||
/* blacklisted */
|
||||
LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i);
|
||||
return 1;
|
||||
} else {
|
||||
/* blacklisted, we have no content sniffing yet */
|
||||
LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* go through the whitelust now */
|
||||
for (i = 0; i < ARRAY_SIZE(white_list); ++i) {
|
||||
/* ignore the rule? */
|
||||
if (white_list[i].filter_dir != FILTER_TO_BOTH
|
||||
&& white_list[i].filter_dir != dir)
|
||||
continue;
|
||||
|
||||
/* the proto is not whitelisted */
|
||||
if (white_list[i].ipa_proto != ALLOW_ANY
|
||||
&& white_list[i].ipa_proto != parsed->ipa_proto)
|
||||
continue;
|
||||
|
||||
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
|
||||
/* the SSN is not whitelisted */
|
||||
if (white_list[i].dest_ssn != ALLOW_ANY
|
||||
&& white_list[i].dest_ssn != parsed->called_ssn)
|
||||
continue;
|
||||
|
||||
/* bssap */
|
||||
if (white_list[i].bssap != ALLOW_ANY
|
||||
&& white_list[i].bssap != parsed->bssap)
|
||||
continue;
|
||||
|
||||
/* gsm */
|
||||
if (white_list[i].gsm != ALLOW_ANY
|
||||
&& white_list[i].gsm != parsed->gsm_type)
|
||||
continue;
|
||||
|
||||
/* whitelisted */
|
||||
LOGP(DNAT, LOGL_INFO, "Whitelisted with rule %d\n", i);
|
||||
return 0;
|
||||
} else {
|
||||
/* whitelisted */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
559
openbsc/src/nat/bsc_mgcp_utils.c
Normal file
559
openbsc/src/nat/bsc_mgcp_utils.c
Normal file
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openbsc/bsc_nat.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/bssap.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/mgcp.h>
|
||||
#include <openbsc/mgcp_internal.h>
|
||||
|
||||
#include <sccp/sccp.h>
|
||||
|
||||
#include <osmocore/talloc.h>
|
||||
#include <osmocore/gsm0808.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int bsc_mgcp_assign(struct sccp_connections *con, struct msgb *msg)
|
||||
{
|
||||
struct sccp_connections *mcon;
|
||||
struct tlv_parsed tp;
|
||||
u_int16_t cic;
|
||||
u_int8_t timeslot;
|
||||
u_int8_t multiplex;
|
||||
int combined;
|
||||
|
||||
if (!msg->l3h) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Assignment message should have l3h pointer.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (msgb_l3len(msg) < 3) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Assignment message has not enough space for GSM0808.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0);
|
||||
if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Circuit identity code not found in assignment message.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cic = ntohs(*(u_int16_t *)TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
|
||||
timeslot = cic & 0x1f;
|
||||
multiplex = (cic & ~0x1f) >> 5;
|
||||
|
||||
|
||||
combined = (32 * multiplex) + timeslot;
|
||||
|
||||
/* find stale connections using that endpoint */
|
||||
llist_for_each_entry(mcon, &con->bsc->nat->sccp_connections, list_entry) {
|
||||
if (mcon->msc_timeslot == combined) {
|
||||
LOGP(DNAT, LOGL_ERROR,
|
||||
"Timeslot %d was assigned to 0x%x and now 0x%x\n",
|
||||
combined,
|
||||
sccp_src_ref_to_int(&mcon->patched_ref),
|
||||
sccp_src_ref_to_int(&con->patched_ref));
|
||||
bsc_mgcp_dlcx(mcon);
|
||||
}
|
||||
}
|
||||
|
||||
con->msc_timeslot = combined;
|
||||
con->bsc_timeslot = con->msc_timeslot;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bsc_mgcp_free_endpoint(struct bsc_nat *nat, int i)
|
||||
{
|
||||
if (nat->bsc_endpoints[i].transaction_id) {
|
||||
talloc_free(nat->bsc_endpoints[i].transaction_id);
|
||||
nat->bsc_endpoints[i].transaction_id = NULL;
|
||||
}
|
||||
|
||||
nat->bsc_endpoints[i].bsc = NULL;
|
||||
}
|
||||
|
||||
void bsc_mgcp_free_endpoints(struct bsc_nat *nat)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < nat->mgcp_cfg->number_endpoints; ++i){
|
||||
bsc_mgcp_free_endpoint(nat, i);
|
||||
mgcp_free_endp(&nat->mgcp_cfg->endpoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* send a MDCX where we do not want a response */
|
||||
static void bsc_mgcp_send_mdcx(struct bsc_connection *bsc, struct mgcp_endpoint *endp)
|
||||
{
|
||||
char buf[2096];
|
||||
int len;
|
||||
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
"MDCX 23 %x@mgw MGCP 1.0\r\n"
|
||||
"Z: noanswer\r\n"
|
||||
"\r\n"
|
||||
"c=IN IP4 %s\r\n"
|
||||
"m=audio %d RTP/AVP 255\r\n",
|
||||
ENDPOINT_NUMBER(endp),
|
||||
bsc->nat->mgcp_cfg->source_addr,
|
||||
endp->rtp_port);
|
||||
if (len < 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "snprintf for DLCX failed.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void bsc_mgcp_send_dlcx(struct bsc_connection *bsc, int endpoint)
|
||||
{
|
||||
char buf[2096];
|
||||
int len;
|
||||
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
"DLCX 23 %x@mgw MGCP 1.0\r\n"
|
||||
"Z: noanswer\r\n", endpoint);
|
||||
if (len < 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "snprintf for DLCX failed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bsc_write_mgcp(bsc, (u_int8_t *) buf, len);
|
||||
}
|
||||
|
||||
void bsc_mgcp_init(struct sccp_connections *con)
|
||||
{
|
||||
con->msc_timeslot = -1;
|
||||
con->bsc_timeslot = -1;
|
||||
con->crcx = 0;
|
||||
}
|
||||
|
||||
void bsc_mgcp_dlcx(struct sccp_connections *con)
|
||||
{
|
||||
/* send a DLCX down the stream */
|
||||
if (con->bsc_timeslot != -1 && con->crcx) {
|
||||
int endp = mgcp_timeslot_to_endpoint(0, con->msc_timeslot);
|
||||
bsc_mgcp_send_dlcx(con->bsc, endp);
|
||||
bsc_mgcp_free_endpoint(con->bsc->nat, endp);
|
||||
}
|
||||
|
||||
bsc_mgcp_init(con);
|
||||
}
|
||||
|
||||
|
||||
struct sccp_connections *bsc_mgcp_find_con(struct bsc_nat *nat, int endpoint)
|
||||
{
|
||||
struct sccp_connections *con = NULL;
|
||||
struct sccp_connections *sccp;
|
||||
|
||||
llist_for_each_entry(sccp, &nat->sccp_connections, list_entry) {
|
||||
if (sccp->msc_timeslot == -1)
|
||||
continue;
|
||||
if (mgcp_timeslot_to_endpoint(0, sccp->msc_timeslot) != endpoint)
|
||||
continue;
|
||||
|
||||
con = sccp;
|
||||
}
|
||||
|
||||
if (con)
|
||||
return con;
|
||||
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to find the connection.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bsc_mgcp_policy_cb(struct mgcp_config *cfg, int endpoint, int state, const char *transaction_id)
|
||||
{
|
||||
struct bsc_nat *nat;
|
||||
struct bsc_endpoint *bsc_endp;
|
||||
struct sccp_connections *sccp;
|
||||
struct mgcp_endpoint *mgcp_endp;
|
||||
struct msgb *bsc_msg;
|
||||
|
||||
nat = cfg->data;
|
||||
bsc_endp = &nat->bsc_endpoints[endpoint];
|
||||
mgcp_endp = &nat->mgcp_cfg->endpoints[endpoint];
|
||||
|
||||
if (bsc_endp->transaction_id) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Endpoint 0x%x had pending transaction: '%s'\n",
|
||||
endpoint, bsc_endp->transaction_id);
|
||||
talloc_free(bsc_endp->transaction_id);
|
||||
bsc_endp->transaction_id = NULL;
|
||||
}
|
||||
bsc_endp->bsc = NULL;
|
||||
|
||||
sccp = bsc_mgcp_find_con(nat, endpoint);
|
||||
|
||||
if (!sccp) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Did not find BSC for change on endpoint: 0x%x state: %d\n", endpoint, state);
|
||||
|
||||
switch (state) {
|
||||
case MGCP_ENDP_CRCX:
|
||||
return MGCP_POLICY_REJECT;
|
||||
break;
|
||||
case MGCP_ENDP_DLCX:
|
||||
return MGCP_POLICY_CONT;
|
||||
break;
|
||||
case MGCP_ENDP_MDCX:
|
||||
return MGCP_POLICY_CONT;
|
||||
break;
|
||||
default:
|
||||
LOGP(DMGCP, LOGL_FATAL, "Unhandled state: %d\n", state);
|
||||
return MGCP_POLICY_CONT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* we need to generate a new and patched message */
|
||||
bsc_msg = bsc_mgcp_rewrite((char *) nat->mgcp_msg, nat->mgcp_length,
|
||||
nat->mgcp_cfg->source_addr, mgcp_endp->rtp_port);
|
||||
if (!bsc_msg) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to patch the msg.\n");
|
||||
return MGCP_POLICY_CONT;
|
||||
}
|
||||
|
||||
|
||||
bsc_endp->transaction_id = talloc_strdup(nat, transaction_id);
|
||||
bsc_endp->bsc = sccp->bsc;
|
||||
|
||||
/* we need to update some bits */
|
||||
if (state == MGCP_ENDP_CRCX) {
|
||||
struct sockaddr_in sock;
|
||||
socklen_t len = sizeof(sock);
|
||||
if (getpeername(sccp->bsc->write_queue.bfd.fd, (struct sockaddr *) &sock, &len) != 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Can not get the peername...%d/%s\n",
|
||||
errno, strerror(errno));
|
||||
} else {
|
||||
mgcp_endp->bts = sock.sin_addr;
|
||||
}
|
||||
|
||||
/* send the message and a fake MDCX for force sending of a dummy packet */
|
||||
sccp->crcx = 1;
|
||||
bsc_write(sccp->bsc, bsc_msg, NAT_IPAC_PROTO_MGCP);
|
||||
bsc_mgcp_send_mdcx(sccp->bsc, mgcp_endp);
|
||||
return MGCP_POLICY_DEFER;
|
||||
} else if (state == MGCP_ENDP_DLCX) {
|
||||
/* we will free the endpoint now and send a DLCX to the BSC */
|
||||
msgb_free(bsc_msg);
|
||||
bsc_mgcp_dlcx(sccp);
|
||||
return MGCP_POLICY_CONT;
|
||||
} else {
|
||||
bsc_write(sccp->bsc, bsc_msg, NAT_IPAC_PROTO_MGCP);
|
||||
return MGCP_POLICY_DEFER;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We have received a msg from the BSC. We will see if we know
|
||||
* this transaction and if it belongs to the BSC. Then we will
|
||||
* need to patch the content to point to the local network and we
|
||||
* need to update the I: that was assigned by the BSS.
|
||||
*/
|
||||
void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg)
|
||||
{
|
||||
struct msgb *output;
|
||||
struct bsc_endpoint *bsc_endp = NULL;
|
||||
struct mgcp_endpoint *endp = NULL;
|
||||
int i, code;
|
||||
char transaction_id[60];
|
||||
|
||||
/* Some assumption that our buffer is big enough.. and null terminate */
|
||||
if (msgb_l2len(msg) > 2000) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "MGCP message too long.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msg->l2h[msgb_l2len(msg)] = '\0';
|
||||
|
||||
if (bsc_mgcp_parse_response((const char *) msg->l2h, &code, transaction_id) != 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to parse response code.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 1; i < bsc->nat->mgcp_cfg->number_endpoints; ++i) {
|
||||
if (bsc->nat->bsc_endpoints[i].bsc != bsc)
|
||||
continue;
|
||||
/* no one listening? a bug? */
|
||||
if (!bsc->nat->bsc_endpoints[i].transaction_id)
|
||||
continue;
|
||||
if (strcmp(transaction_id, bsc->nat->bsc_endpoints[i].transaction_id) != 0)
|
||||
continue;
|
||||
|
||||
endp = &bsc->nat->mgcp_cfg->endpoints[i];
|
||||
bsc_endp = &bsc->nat->bsc_endpoints[i];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bsc_endp) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Could not find active endpoint: %s for msg: '%s'\n",
|
||||
transaction_id, (const char *) msg->l2h);
|
||||
return;
|
||||
}
|
||||
|
||||
endp->ci = bsc_mgcp_extract_ci((const char *) msg->l2h);
|
||||
|
||||
/* free some stuff */
|
||||
talloc_free(bsc_endp->transaction_id);
|
||||
bsc_endp->transaction_id = NULL;
|
||||
|
||||
/*
|
||||
* rewrite the information. In case the endpoint was deleted
|
||||
* there should be nothing for us to rewrite so putting endp->rtp_port
|
||||
* with the value of 0 should be no problem.
|
||||
*/
|
||||
output = bsc_mgcp_rewrite((char * ) msg->l2h, msgb_l2len(msg),
|
||||
bsc->nat->mgcp_cfg->source_addr, endp->rtp_port);
|
||||
|
||||
if (!output) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to rewrite MGCP msg.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (write_queue_enqueue(&bsc->nat->mgcp_queue, output) != 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to queue MGCP msg.\n");
|
||||
msgb_free(output);
|
||||
}
|
||||
}
|
||||
|
||||
int bsc_mgcp_parse_response(const char *str, int *code, char transaction[60])
|
||||
{
|
||||
/* we want to parse two strings */
|
||||
return sscanf(str, "%3d %59s\n", code, transaction) != 2;
|
||||
}
|
||||
|
||||
int bsc_mgcp_extract_ci(const char *str)
|
||||
{
|
||||
int ci;
|
||||
char *res = strstr(str, "I: ");
|
||||
if (!res)
|
||||
return CI_UNUSED;
|
||||
|
||||
if (sscanf(res, "I: %d", &ci) != 1)
|
||||
return CI_UNUSED;
|
||||
return ci;
|
||||
}
|
||||
|
||||
/* we need to replace some strings... */
|
||||
struct msgb *bsc_mgcp_rewrite(char *input, int length, const char *ip, int port)
|
||||
{
|
||||
static const char *ip_str = "c=IN IP4 ";
|
||||
static const char *aud_str = "m=audio ";
|
||||
|
||||
char buf[128];
|
||||
char *running, *token;
|
||||
struct msgb *output;
|
||||
|
||||
if (length > 4096 - 128) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Input is too long.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
output = msgb_alloc_headroom(4096, 128, "MGCP rewritten");
|
||||
if (!output) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to allocate new MGCP msg.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
running = input;
|
||||
output->l2h = output->data;
|
||||
for (token = strsep(&running, "\n"); running; token = strsep(&running, "\n")) {
|
||||
int len = strlen(token);
|
||||
int cr = len > 0 && token[len - 1] == '\r';
|
||||
|
||||
if (strncmp(ip_str, token, (sizeof ip_str) - 1) == 0) {
|
||||
output->l3h = msgb_put(output, strlen(ip_str));
|
||||
memcpy(output->l3h, ip_str, strlen(ip_str));
|
||||
output->l3h = msgb_put(output, strlen(ip));
|
||||
memcpy(output->l3h, ip, strlen(ip));
|
||||
|
||||
if (cr) {
|
||||
output->l3h = msgb_put(output, 2);
|
||||
output->l3h[0] = '\r';
|
||||
output->l3h[1] = '\n';
|
||||
} else {
|
||||
output->l3h = msgb_put(output, 1);
|
||||
output->l3h[0] = '\n';
|
||||
}
|
||||
} else if (strncmp(aud_str, token, (sizeof aud_str) - 1) == 0) {
|
||||
int payload;
|
||||
if (sscanf(token, "m=audio %*d RTP/AVP %d", &payload) != 1) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Could not parsed audio line.\n");
|
||||
msgb_free(output);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf)-1, "m=audio %d RTP/AVP %d%s",
|
||||
port, payload, cr ? "\r\n" : "\n");
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
|
||||
output->l3h = msgb_put(output, strlen(buf));
|
||||
memcpy(output->l3h, buf, strlen(buf));
|
||||
} else {
|
||||
output->l3h = msgb_put(output, len + 1);
|
||||
memcpy(output->l3h, token, len);
|
||||
output->l3h[len] = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static int mgcp_do_read(struct bsc_fd *fd)
|
||||
{
|
||||
struct bsc_nat *nat;
|
||||
struct msgb *msg, *resp;
|
||||
int rc;
|
||||
|
||||
nat = fd->data;
|
||||
|
||||
rc = read(fd->fd, nat->mgcp_msg, sizeof(nat->mgcp_msg) - 1);
|
||||
if (rc <= 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to read errno: %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
nat->mgcp_msg[rc] = '\0';
|
||||
nat->mgcp_length = rc;
|
||||
|
||||
msg = msgb_alloc(sizeof(nat->mgcp_msg), "MGCP GW Read");
|
||||
if (!msg) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to create buffer.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg->l2h = msgb_put(msg, rc);
|
||||
memcpy(msg->l2h, nat->mgcp_msg, msgb_l2len(msg));
|
||||
resp = mgcp_handle_message(nat->mgcp_cfg, msg);
|
||||
msgb_free(msg);
|
||||
|
||||
/* we do have a direct answer... e.g. AUEP */
|
||||
if (resp) {
|
||||
if (write_queue_enqueue(&nat->mgcp_queue, resp) != 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to enqueue msg.\n");
|
||||
msgb_free(resp);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mgcp_do_write(struct bsc_fd *bfd, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = write(bfd->fd, msg->data, msg->len);
|
||||
|
||||
if (rc != msg->len) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to write msg to MGCP CallAgent.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int bsc_mgcp_nat_init(struct bsc_nat *nat)
|
||||
{
|
||||
int on;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
if (!nat->mgcp_cfg->call_agent_addr) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "The BSC nat requires the call agent ip to be set.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nat->mgcp_cfg->bts_ip) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Do not set the BTS ip for the nat.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
nat->mgcp_queue.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (nat->mgcp_queue.bfd.fd < 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to create MGCP socket. errno: %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
on = 1;
|
||||
setsockopt(nat->mgcp_queue.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(nat->mgcp_cfg->source_port);
|
||||
inet_aton(nat->mgcp_cfg->source_addr, &addr.sin_addr);
|
||||
|
||||
if (bind(nat->mgcp_queue.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to bind. errno: %d\n", errno);
|
||||
close(nat->mgcp_queue.bfd.fd);
|
||||
nat->mgcp_queue.bfd.fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr.sin_port = htons(2727);
|
||||
inet_aton(nat->mgcp_cfg->call_agent_addr, &addr.sin_addr);
|
||||
if (connect(nat->mgcp_queue.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
|
||||
nat->mgcp_cfg->call_agent_addr, errno);
|
||||
close(nat->mgcp_queue.bfd.fd);
|
||||
nat->mgcp_queue.bfd.fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
write_queue_init(&nat->mgcp_queue, 10);
|
||||
nat->mgcp_queue.bfd.when = BSC_FD_READ;
|
||||
nat->mgcp_queue.bfd.data = nat;
|
||||
nat->mgcp_queue.read_cb = mgcp_do_read;
|
||||
nat->mgcp_queue.write_cb = mgcp_do_write;
|
||||
|
||||
if (bsc_register_fd(&nat->mgcp_queue.bfd) != 0) {
|
||||
LOGP(DMGCP, LOGL_ERROR, "Failed to register MGCP fd.\n");
|
||||
close(nat->mgcp_queue.bfd.fd);
|
||||
nat->mgcp_queue.bfd.fd = -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* some more MGCP config handling */
|
||||
nat->mgcp_cfg->audio_payload = -1;
|
||||
nat->mgcp_cfg->data = nat;
|
||||
nat->mgcp_cfg->policy_cb = bsc_mgcp_policy_cb;
|
||||
nat->mgcp_cfg->force_realloc = 1;
|
||||
nat->mgcp_cfg->bts_ip = "";
|
||||
nat->bsc_endpoints = talloc_zero_array(nat,
|
||||
struct bsc_endpoint,
|
||||
nat->mgcp_cfg->number_endpoints + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < bsc->nat->mgcp_cfg->number_endpoints; ++i) {
|
||||
struct bsc_endpoint *bsc_endp = &bsc->nat->bsc_endpoints[i];
|
||||
|
||||
if (bsc_endp->bsc != bsc)
|
||||
continue;
|
||||
|
||||
bsc_mgcp_free_endpoint(bsc->nat, i);
|
||||
mgcp_free_endp(&bsc->nat->mgcp_cfg->endpoints[i]);
|
||||
}
|
||||
}
|
||||
989
openbsc/src/nat/bsc_nat.c
Normal file
989
openbsc/src/nat/bsc_nat.c
Normal file
@@ -0,0 +1,989 @@
|
||||
/* BSC Multiplexer/NAT */
|
||||
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <getopt.h>
|
||||
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/bsc_msc.h>
|
||||
#include <openbsc/bsc_nat.h>
|
||||
#include <openbsc/bssap.h>
|
||||
#include <openbsc/ipaccess.h>
|
||||
#include <openbsc/abis_nm.h>
|
||||
#include <openbsc/telnet_interface.h>
|
||||
|
||||
#include <osmocore/talloc.h>
|
||||
|
||||
#include <vty/vty.h>
|
||||
|
||||
#include <sccp/sccp.h>
|
||||
|
||||
struct log_target *stderr_target;
|
||||
static const char *config_file = "bsc-nat.cfg";
|
||||
static struct in_addr local_addr;
|
||||
static struct bsc_fd bsc_listen;
|
||||
static const char *msc_ip = NULL;
|
||||
|
||||
|
||||
static struct bsc_nat *nat;
|
||||
static void bsc_send_data(struct bsc_connection *bsc, const u_int8_t *data, unsigned int length, int);
|
||||
static void msc_send_reset(struct bsc_msc_connection *con);
|
||||
|
||||
struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num)
|
||||
{
|
||||
struct bsc_config *conf;
|
||||
|
||||
llist_for_each_entry(conf, &nat->bsc_configs, entry)
|
||||
if (conf->nr == num)
|
||||
return conf;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* below are stubs we need to link
|
||||
*/
|
||||
int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
|
||||
struct gsm_nm_state *old_state, struct gsm_nm_state *new_state)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
void input_event(int event, enum e1inp_sign_type type, struct gsm_bts_trx *trx)
|
||||
{}
|
||||
|
||||
int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void queue_for_msc(struct bsc_msc_connection *con, struct msgb *msg)
|
||||
{
|
||||
if (write_queue_enqueue(&nat->msc_con->write_queue, msg) != 0) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to enqueue the write.\n");
|
||||
msgb_free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void send_reset_ack(struct bsc_connection *bsc)
|
||||
{
|
||||
static const u_int8_t gsm_reset_ack[] = {
|
||||
0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01,
|
||||
0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03,
|
||||
0x00, 0x01, 0x31,
|
||||
};
|
||||
|
||||
bsc_send_data(bsc, gsm_reset_ack, sizeof(gsm_reset_ack), IPAC_PROTO_SCCP);
|
||||
}
|
||||
|
||||
static void send_ping(struct bsc_connection *bsc)
|
||||
{
|
||||
static const u_int8_t id_ping[] = {
|
||||
IPAC_MSGT_PING,
|
||||
};
|
||||
|
||||
bsc_send_data(bsc, id_ping, sizeof(id_ping), IPAC_PROTO_IPACCESS);
|
||||
}
|
||||
|
||||
static void send_pong(struct bsc_connection *bsc)
|
||||
{
|
||||
static const u_int8_t id_pong[] = {
|
||||
IPAC_MSGT_PONG,
|
||||
};
|
||||
|
||||
bsc_send_data(bsc, id_pong, sizeof(id_pong), IPAC_PROTO_IPACCESS);
|
||||
}
|
||||
|
||||
static void bsc_pong_timeout(void *_bsc)
|
||||
{
|
||||
struct bsc_connection *bsc = _bsc;
|
||||
|
||||
LOGP(DNAT, LOGL_ERROR, "BSC Nr: %d PONG timeout.\n", bsc->cfg->nr);
|
||||
bsc_close_connection(bsc);
|
||||
}
|
||||
|
||||
static void bsc_ping_timeout(void *_bsc)
|
||||
{
|
||||
struct bsc_connection *bsc = _bsc;
|
||||
|
||||
if (bsc->nat->ping_timeout < 0)
|
||||
return;
|
||||
|
||||
send_ping(bsc);
|
||||
|
||||
/* send another ping in 20 seconds */
|
||||
bsc_schedule_timer(&bsc->ping_timeout, bsc->nat->ping_timeout, 0);
|
||||
|
||||
/* also start a pong timer */
|
||||
bsc_schedule_timer(&bsc->pong_timeout, bsc->nat->pong_timeout, 0);
|
||||
}
|
||||
|
||||
static void start_ping_pong(struct bsc_connection *bsc)
|
||||
{
|
||||
bsc->pong_timeout.data = bsc;
|
||||
bsc->pong_timeout.cb = bsc_pong_timeout;
|
||||
bsc->ping_timeout.data = bsc;
|
||||
bsc->ping_timeout.cb = bsc_ping_timeout;
|
||||
|
||||
bsc_ping_timeout(bsc);
|
||||
}
|
||||
|
||||
static void send_id_ack(struct bsc_connection *bsc)
|
||||
{
|
||||
static const u_int8_t id_ack[] = {
|
||||
IPAC_MSGT_ID_ACK
|
||||
};
|
||||
|
||||
bsc_send_data(bsc, id_ack, sizeof(id_ack), IPAC_PROTO_IPACCESS);
|
||||
}
|
||||
|
||||
static void send_id_req(struct bsc_connection *bsc)
|
||||
{
|
||||
static const u_int8_t id_req[] = {
|
||||
IPAC_MSGT_ID_GET,
|
||||
0x01, IPAC_IDTAG_UNIT,
|
||||
0x01, IPAC_IDTAG_MACADDR,
|
||||
0x01, IPAC_IDTAG_LOCATION1,
|
||||
0x01, IPAC_IDTAG_LOCATION2,
|
||||
0x01, IPAC_IDTAG_EQUIPVERS,
|
||||
0x01, IPAC_IDTAG_SWVERSION,
|
||||
0x01, IPAC_IDTAG_UNITNAME,
|
||||
0x01, IPAC_IDTAG_SERNR,
|
||||
};
|
||||
|
||||
bsc_send_data(bsc, id_req, sizeof(id_req), IPAC_PROTO_IPACCESS);
|
||||
}
|
||||
|
||||
static void nat_send_rlsd(struct sccp_connections *conn)
|
||||
{
|
||||
struct sccp_connection_released *rel;
|
||||
struct msgb *msg;
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "rlsd");
|
||||
if (!msg) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msg->l2h = msgb_put(msg, sizeof(*rel));
|
||||
rel = (struct sccp_connection_released *) msg->l2h;
|
||||
rel->type = SCCP_MSG_TYPE_RLSD;
|
||||
rel->release_cause = SCCP_RELEASE_CAUSE_SCCP_FAILURE;
|
||||
rel->destination_local_reference = conn->remote_ref;
|
||||
rel->source_local_reference = conn->patched_ref;
|
||||
|
||||
ipaccess_prepend_header(msg, IPAC_PROTO_SCCP);
|
||||
|
||||
queue_for_msc(nat->msc_con, msg);
|
||||
}
|
||||
|
||||
static void nat_send_rlc(struct sccp_source_reference *src,
|
||||
struct sccp_source_reference *dst)
|
||||
{
|
||||
struct sccp_connection_release_complete *rlc;
|
||||
struct msgb *msg;
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "rlc");
|
||||
if (!msg) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msg->l2h = msgb_put(msg, sizeof(*rlc));
|
||||
rlc = (struct sccp_connection_release_complete *) msg->l2h;
|
||||
rlc->type = SCCP_MSG_TYPE_RLC;
|
||||
rlc->destination_local_reference = *dst;
|
||||
rlc->source_local_reference = *src;
|
||||
|
||||
ipaccess_prepend_header(msg, IPAC_PROTO_SCCP);
|
||||
|
||||
queue_for_msc(nat->msc_con, msg);
|
||||
}
|
||||
|
||||
static void send_mgcp_reset(struct bsc_connection *bsc)
|
||||
{
|
||||
static const u_int8_t mgcp_reset[] = {
|
||||
"RSIP 1 13@mgw MGCP 1.0\r\n"
|
||||
};
|
||||
|
||||
bsc_write_mgcp(bsc, mgcp_reset, sizeof mgcp_reset - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Below is the handling of messages coming
|
||||
* from the MSC and need to be forwarded to
|
||||
* a real BSC.
|
||||
*/
|
||||
static void initialize_msc_if_needed()
|
||||
{
|
||||
if (nat->first_contact)
|
||||
return;
|
||||
|
||||
nat->first_contact = 1;
|
||||
msc_send_reset(nat->msc_con);
|
||||
}
|
||||
|
||||
/*
|
||||
* Currently we are lacking refcounting so we need to copy each message.
|
||||
*/
|
||||
static void bsc_send_data(struct bsc_connection *bsc, const u_int8_t *data, unsigned int length, int proto)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
if (length > 4096 - 128) {
|
||||
LOGP(DINP, LOGL_ERROR, "Can not send message of that size.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "to-bsc");
|
||||
if (!msg) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msg->l2h = msgb_put(msg, length);
|
||||
memcpy(msg->data, data, length);
|
||||
|
||||
bsc_write(bsc, msg, proto);
|
||||
}
|
||||
|
||||
static int forward_sccp_to_bts(struct msgb *msg)
|
||||
{
|
||||
struct sccp_connections *con;
|
||||
struct bsc_connection *bsc;
|
||||
struct bsc_nat_parsed *parsed;
|
||||
int proto;
|
||||
|
||||
/* filter, drop, patch the message? */
|
||||
parsed = bsc_nat_parse(msg);
|
||||
if (!parsed) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bsc_nat_filter_ipa(DIR_BSC, msg, parsed))
|
||||
goto exit;
|
||||
|
||||
proto = parsed->ipa_proto;
|
||||
|
||||
/* Route and modify the SCCP packet */
|
||||
if (proto == IPAC_PROTO_SCCP) {
|
||||
switch (parsed->sccp_type) {
|
||||
case SCCP_MSG_TYPE_UDT:
|
||||
/* forward UDT messages to every BSC */
|
||||
goto send_to_all;
|
||||
break;
|
||||
case SCCP_MSG_TYPE_RLSD:
|
||||
case SCCP_MSG_TYPE_CREF:
|
||||
case SCCP_MSG_TYPE_DT1:
|
||||
case SCCP_MSG_TYPE_IT:
|
||||
con = patch_sccp_src_ref_to_bsc(msg, parsed, nat);
|
||||
if (parsed->gsm_type == BSS_MAP_MSG_ASSIGMENT_RQST) {
|
||||
counter_inc(nat->stats.sccp.calls);
|
||||
|
||||
if (con) {
|
||||
counter_inc(con->bsc->cfg->stats.sccp.calls);
|
||||
if (bsc_mgcp_assign(con, msg) != 0)
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to assign...\n");
|
||||
} else
|
||||
LOGP(DNAT, LOGL_ERROR, "Assignment command but no BSC.\n");
|
||||
}
|
||||
break;
|
||||
case SCCP_MSG_TYPE_CC:
|
||||
con = patch_sccp_src_ref_to_bsc(msg, parsed, nat);
|
||||
if (!con || update_sccp_src_ref(con, parsed) != 0)
|
||||
goto exit;
|
||||
break;
|
||||
case SCCP_MSG_TYPE_RLC:
|
||||
LOGP(DNAT, LOGL_ERROR, "Unexpected release complete from MSC.\n");
|
||||
goto exit;
|
||||
break;
|
||||
case SCCP_MSG_TYPE_CR:
|
||||
/* MSC never opens a SCCP connection, fall through */
|
||||
default:
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!con && parsed->sccp_type == SCCP_MSG_TYPE_RLSD) {
|
||||
LOGP(DNAT, LOGL_NOTICE, "Sending fake RLC on RLSD message to network.\n");
|
||||
/* Exchange src/dest for the reply */
|
||||
nat_send_rlc(parsed->dest_local_ref, parsed->src_local_ref);
|
||||
} else if (!con)
|
||||
LOGP(DNAT, LOGL_ERROR, "Unknown connection for msg type: 0x%x.\n", parsed->sccp_type);
|
||||
}
|
||||
|
||||
talloc_free(parsed);
|
||||
if (!con)
|
||||
return -1;
|
||||
if (!con->bsc->authenticated) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Selected BSC not authenticated.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bsc_send_data(con->bsc, msg->l2h, msgb_l2len(msg), proto);
|
||||
return 0;
|
||||
|
||||
send_to_all:
|
||||
/*
|
||||
* Filter Paging from the network. We do not want to send a PAGING
|
||||
* Command to every BSC in our network. We will analys the PAGING
|
||||
* message and then send it to the authenticated messages...
|
||||
*/
|
||||
if (parsed->ipa_proto == IPAC_PROTO_SCCP && parsed->gsm_type == BSS_MAP_MSG_PAGING) {
|
||||
int lac;
|
||||
bsc = bsc_nat_find_bsc(nat, msg, &lac);
|
||||
if (bsc && bsc->cfg->forbid_paging)
|
||||
LOGP(DNAT, LOGL_DEBUG, "Paging forbidden for BTS: %d\n", bsc->cfg->nr);
|
||||
else if (bsc)
|
||||
bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), parsed->ipa_proto);
|
||||
else
|
||||
LOGP(DNAT, LOGL_ERROR, "Could not determine BSC for paging on lac: %d/0x%x\n",
|
||||
lac, lac);
|
||||
|
||||
goto exit;
|
||||
}
|
||||
/* currently send this to every BSC connected */
|
||||
llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) {
|
||||
if (!bsc->authenticated)
|
||||
continue;
|
||||
|
||||
bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), parsed->ipa_proto);
|
||||
}
|
||||
|
||||
exit:
|
||||
talloc_free(parsed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msc_connection_was_lost(struct bsc_msc_connection *con)
|
||||
{
|
||||
struct bsc_connection *bsc, *tmp;
|
||||
|
||||
counter_inc(nat->stats.msc.reconn);
|
||||
|
||||
LOGP(DMSC, LOGL_ERROR, "Closing all connections downstream.\n");
|
||||
llist_for_each_entry_safe(bsc, tmp, &nat->bsc_connections, list_entry)
|
||||
bsc_close_connection(bsc);
|
||||
|
||||
nat->first_contact = 0;
|
||||
bsc_mgcp_free_endpoints(nat);
|
||||
bsc_msc_schedule_connect(con);
|
||||
}
|
||||
|
||||
static void msc_send_reset(struct bsc_msc_connection *msc_con)
|
||||
{
|
||||
static const u_int8_t reset[] = {
|
||||
0x00, 0x12, 0xfd,
|
||||
0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe,
|
||||
0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04,
|
||||
0x01, 0x20
|
||||
};
|
||||
|
||||
struct msgb *msg;
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "08.08 reset");
|
||||
if (!msg) {
|
||||
LOGP(DMSC, LOGL_ERROR, "Failed to allocate reset msg.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
msg->l2h = msgb_put(msg, sizeof(reset));
|
||||
memcpy(msg->l2h, reset, msgb_l2len(msg));
|
||||
|
||||
queue_for_msc(nat->msc_con, msg);
|
||||
|
||||
LOGP(DMSC, LOGL_NOTICE, "Scheduled GSM0808 reset msg for the MSC.\n");
|
||||
}
|
||||
|
||||
static int ipaccess_msc_read_cb(struct bsc_fd *bfd)
|
||||
{
|
||||
int error;
|
||||
struct msgb *msg = ipaccess_read_msg(bfd, &error);
|
||||
struct ipaccess_head *hh;
|
||||
|
||||
if (!msg) {
|
||||
if (error == 0)
|
||||
LOGP(DNAT, LOGL_FATAL, "The connection the MSC was lost, exiting\n");
|
||||
else
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to parse ip access message: %d\n", error);
|
||||
|
||||
bsc_msc_lost(nat->msc_con);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOGP(DNAT, LOGL_DEBUG, "MSG from MSC: %s proto: %d\n", hexdump(msg->data, msg->len), msg->l2h[0]);
|
||||
|
||||
/* handle base message handling */
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
ipaccess_rcvmsg_base(msg, bfd);
|
||||
|
||||
/* initialize the networking. This includes sending a GSM08.08 message */
|
||||
if (hh->proto == IPAC_PROTO_IPACCESS && msg->l2h[0] == IPAC_MSGT_ID_ACK)
|
||||
initialize_msc_if_needed();
|
||||
else if (hh->proto == IPAC_PROTO_SCCP)
|
||||
forward_sccp_to_bts(msg);
|
||||
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipaccess_msc_write_cb(struct bsc_fd *bfd, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
rc = write(bfd->fd, msg->data, msg->len);
|
||||
|
||||
if (rc != msg->len) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to write MSG to MSC.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Below is the handling of messages coming
|
||||
* from the BSC and need to be forwarded to
|
||||
* a real BSC.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Remove the connection from the connections list,
|
||||
* remove it from the patching of SCCP header lists
|
||||
* as well. Maybe in the future even close connection..
|
||||
*/
|
||||
void bsc_close_connection(struct bsc_connection *connection)
|
||||
{
|
||||
struct sccp_connections *sccp_patch, *tmp;
|
||||
|
||||
/* stop the timeout timer */
|
||||
bsc_del_timer(&connection->id_timeout);
|
||||
bsc_del_timer(&connection->ping_timeout);
|
||||
bsc_del_timer(&connection->pong_timeout);
|
||||
|
||||
/* remove all SCCP connections */
|
||||
llist_for_each_entry_safe(sccp_patch, tmp, &nat->sccp_connections, list_entry) {
|
||||
if (sccp_patch->bsc != connection)
|
||||
continue;
|
||||
|
||||
if (sccp_patch->has_remote_ref)
|
||||
nat_send_rlsd(sccp_patch);
|
||||
sccp_connection_destroy(sccp_patch);
|
||||
}
|
||||
|
||||
/* close endpoints allocated by this BSC */
|
||||
bsc_mgcp_clear_endpoints_for(connection);
|
||||
|
||||
bsc_unregister_fd(&connection->write_queue.bfd);
|
||||
close(connection->write_queue.bfd.fd);
|
||||
write_queue_clear(&connection->write_queue);
|
||||
llist_del(&connection->list_entry);
|
||||
|
||||
talloc_free(connection);
|
||||
}
|
||||
|
||||
static void ipaccess_close_bsc(void *data)
|
||||
{
|
||||
struct sockaddr_in sock;
|
||||
socklen_t len = sizeof(sock);
|
||||
struct bsc_connection *conn = data;
|
||||
|
||||
|
||||
getpeername(conn->write_queue.bfd.fd, (struct sockaddr *) &sock, &len);
|
||||
LOGP(DNAT, LOGL_ERROR, "BSC on %s didn't respond to identity request. Closing.\n",
|
||||
inet_ntoa(sock.sin_addr));
|
||||
bsc_close_connection(conn);
|
||||
}
|
||||
|
||||
static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc)
|
||||
{
|
||||
struct bsc_config *conf;
|
||||
const char* token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME);
|
||||
|
||||
if (bsc->cfg) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Reauth on fd %d bsc nr %d\n",
|
||||
bsc->write_queue.bfd.fd, bsc->cfg->nr);
|
||||
return;
|
||||
}
|
||||
|
||||
llist_for_each_entry(conf, &bsc->nat->bsc_configs, entry) {
|
||||
if (strcmp(conf->token, token) == 0) {
|
||||
counter_inc(conf->stats.net.reconn);
|
||||
bsc->authenticated = 1;
|
||||
bsc->cfg = conf;
|
||||
bsc_del_timer(&bsc->id_timeout);
|
||||
LOGP(DNAT, LOGL_NOTICE, "Authenticated bsc nr: %d lac: %d on fd %d\n",
|
||||
conf->nr, conf->lac, bsc->write_queue.bfd.fd);
|
||||
start_ping_pong(bsc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DNAT, LOGL_ERROR, "No bsc found for token %s on fd: %d.\n", token,
|
||||
bsc->write_queue.bfd.fd);
|
||||
}
|
||||
|
||||
static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg)
|
||||
{
|
||||
struct sccp_connections *con;
|
||||
struct bsc_nat_parsed *parsed;
|
||||
|
||||
/* Parse and filter messages */
|
||||
parsed = bsc_nat_parse(msg);
|
||||
if (!parsed) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bsc_nat_filter_ipa(DIR_MSC, msg, parsed))
|
||||
goto exit;
|
||||
|
||||
/*
|
||||
* check authentication after filtering to not reject auth
|
||||
* responses coming from the BSC. We have to make sure that
|
||||
* nothing from the exit path will forward things to the MSC
|
||||
*/
|
||||
if (!bsc->authenticated) {
|
||||
LOGP(DNAT, LOGL_ERROR, "BSC is not authenticated.\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* modify the SCCP entries */
|
||||
if (parsed->ipa_proto == IPAC_PROTO_SCCP) {
|
||||
switch (parsed->sccp_type) {
|
||||
case SCCP_MSG_TYPE_CR:
|
||||
if (create_sccp_src_ref(bsc, msg, parsed) != 0)
|
||||
goto exit2;
|
||||
con = patch_sccp_src_ref_to_msc(msg, parsed, bsc);
|
||||
break;
|
||||
case SCCP_MSG_TYPE_RLSD:
|
||||
case SCCP_MSG_TYPE_CREF:
|
||||
case SCCP_MSG_TYPE_DT1:
|
||||
case SCCP_MSG_TYPE_CC:
|
||||
case SCCP_MSG_TYPE_IT:
|
||||
con = patch_sccp_src_ref_to_msc(msg, parsed, bsc);
|
||||
break;
|
||||
case SCCP_MSG_TYPE_RLC:
|
||||
con = patch_sccp_src_ref_to_msc(msg, parsed, bsc);
|
||||
remove_sccp_src_ref(bsc, msg, parsed);
|
||||
break;
|
||||
case SCCP_MSG_TYPE_UDT:
|
||||
/* simply forward everything */
|
||||
con = NULL;
|
||||
break;
|
||||
default:
|
||||
LOGP(DNAT, LOGL_ERROR, "Not forwarding to msc sccp type: 0x%x\n", parsed->sccp_type);
|
||||
con = NULL;
|
||||
goto exit2;
|
||||
break;
|
||||
}
|
||||
} else if (parsed->ipa_proto == NAT_IPAC_PROTO_MGCP) {
|
||||
bsc_mgcp_forward(bsc, msg);
|
||||
goto exit2;
|
||||
} else {
|
||||
LOGP(DNAT, LOGL_ERROR, "Not forwarding unknown stream id: 0x%x\n", parsed->ipa_proto);
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
if (con && con->bsc != bsc) {
|
||||
LOGP(DNAT, LOGL_ERROR, "The connection belongs to a different BTS: input: %d con: %d\n",
|
||||
bsc->cfg->nr, con->bsc->cfg->nr);
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
/* send the non-filtered but maybe modified msg */
|
||||
queue_for_msc(nat->msc_con, msg);
|
||||
talloc_free(parsed);
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
/* if we filter out the reset send an ack to the BSC */
|
||||
if (parsed->bssap == 0 && parsed->gsm_type == BSS_MAP_MSG_RESET) {
|
||||
send_reset_ack(bsc);
|
||||
send_reset_ack(bsc);
|
||||
} else if (parsed->ipa_proto == IPAC_PROTO_IPACCESS) {
|
||||
/* do we know who is handling this? */
|
||||
if (msg->l2h[0] == IPAC_MSGT_ID_RESP) {
|
||||
struct tlv_parsed tvp;
|
||||
ipaccess_idtag_parse(&tvp,
|
||||
(unsigned char *) msg->l2h + 2,
|
||||
msgb_l2len(msg) - 2);
|
||||
if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME))
|
||||
ipaccess_auth_bsc(&tvp, bsc);
|
||||
}
|
||||
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
exit2:
|
||||
talloc_free(parsed);
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ipaccess_bsc_read_cb(struct bsc_fd *bfd)
|
||||
{
|
||||
int error;
|
||||
struct bsc_connection *bsc = bfd->data;
|
||||
struct msgb *msg = ipaccess_read_msg(bfd, &error);
|
||||
struct ipaccess_head *hh;
|
||||
|
||||
if (!msg) {
|
||||
if (error == 0)
|
||||
LOGP(DNAT, LOGL_ERROR,
|
||||
"The connection to the BSC Nr: %d was lost. Cleaning it\n",
|
||||
bsc->cfg ? bsc->cfg->nr : -1);
|
||||
else
|
||||
LOGP(DNAT, LOGL_ERROR,
|
||||
"Stream error on BSC Nr: %d. Failed to parse ip access message: %d\n",
|
||||
bsc->cfg ? bsc->cfg->nr : -1, error);
|
||||
|
||||
bsc_close_connection(bsc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
LOGP(DNAT, LOGL_DEBUG, "MSG from BSC: %s proto: %d\n", hexdump(msg->data, msg->len), msg->l2h[0]);
|
||||
|
||||
/* Handle messages from the BSC */
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
|
||||
/* stop the pong timeout */
|
||||
if (hh->proto == IPAC_PROTO_IPACCESS) {
|
||||
if (msg->l2h[0] == IPAC_MSGT_PONG) {
|
||||
bsc_del_timer(&bsc->pong_timeout);
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
} else if (msg->l2h[0] == IPAC_MSGT_PING) {
|
||||
send_pong(bsc);
|
||||
msgb_free(msg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: Currently no PONG is sent to the BSC */
|
||||
/* FIXME: Currently no ID ACK is sent to the BSC */
|
||||
forward_sccp_to_msc(bsc, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipaccess_bsc_write_cb(struct bsc_fd *bfd, struct msgb *msg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = write(bfd->fd, msg->data, msg->len);
|
||||
if (rc != msg->len)
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to write message to the BSC.\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int ipaccess_listen_bsc_cb(struct bsc_fd *bfd, unsigned int what)
|
||||
{
|
||||
struct bsc_connection *bsc;
|
||||
int fd, rc, on;
|
||||
struct sockaddr_in sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
|
||||
if (!(what & BSC_FD_READ))
|
||||
return 0;
|
||||
|
||||
fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len);
|
||||
if (fd < 0) {
|
||||
perror("accept");
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* count the reconnect */
|
||||
counter_inc(nat->stats.bsc.reconn);
|
||||
|
||||
/*
|
||||
* if we are not connected to a msc... just close the socket
|
||||
*/
|
||||
if (!nat->msc_con->is_connected) {
|
||||
LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due lack of MSC connection.\n");
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
on = 1;
|
||||
rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
|
||||
if (rc != 0)
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
|
||||
|
||||
/* todo... do something with the connection */
|
||||
/* todo... use GNUtls to see if we want to trust this as a BTS */
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
bsc = bsc_connection_alloc(nat);
|
||||
if (!bsc) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to allocate BSC struct.\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bsc->write_queue.bfd.data = bsc;
|
||||
bsc->write_queue.bfd.fd = fd;
|
||||
bsc->write_queue.read_cb = ipaccess_bsc_read_cb;
|
||||
bsc->write_queue.write_cb = ipaccess_bsc_write_cb;
|
||||
bsc->write_queue.bfd.when = BSC_FD_READ;
|
||||
if (bsc_register_fd(&bsc->write_queue.bfd) < 0) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to register BSC fd.\n");
|
||||
close(fd);
|
||||
talloc_free(bsc);
|
||||
return -2;
|
||||
}
|
||||
|
||||
LOGP(DNAT, LOGL_NOTICE, "BSC connection on %d with IP: %s\n",
|
||||
fd, inet_ntoa(sa.sin_addr));
|
||||
llist_add(&bsc->list_entry, &nat->bsc_connections);
|
||||
send_id_ack(bsc);
|
||||
send_id_req(bsc);
|
||||
send_mgcp_reset(bsc);
|
||||
|
||||
/*
|
||||
* start the hangup timer
|
||||
*/
|
||||
bsc->id_timeout.data = bsc;
|
||||
bsc->id_timeout.cb = ipaccess_close_bsc;
|
||||
bsc_schedule_timer(&bsc->id_timeout, nat->auth_timeout, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int listen_for_bsc(struct bsc_fd *bfd, struct in_addr *in_addr, int port)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int ret, on = 1;
|
||||
|
||||
bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
bfd->cb = ipaccess_listen_bsc_cb;
|
||||
bfd->when = BSC_FD_READ;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = in_addr->s_addr;
|
||||
|
||||
setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
|
||||
ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Could not bind the BSC socket %s\n",
|
||||
strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = listen(bfd->fd, 1);
|
||||
if (ret < 0) {
|
||||
perror("listen");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = bsc_register_fd(bfd);
|
||||
if (ret < 0) {
|
||||
perror("register_listen_fd");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_usage()
|
||||
{
|
||||
printf("Usage: bsc_nat\n");
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
printf(" Some useful help...\n");
|
||||
printf(" -h --help this text\n");
|
||||
printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n");
|
||||
printf(" -s --disable-color\n");
|
||||
printf(" -c --config-file filename The config file to use.\n");
|
||||
printf(" -m --msc=IP. The address of the MSC.\n");
|
||||
printf(" -l --local=IP. The local address of this BSC.\n");
|
||||
}
|
||||
|
||||
static void handle_options(int argc, char** argv)
|
||||
{
|
||||
while (1) {
|
||||
int option_index = 0, c;
|
||||
static struct option long_options[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
{"debug", 1, 0, 'd'},
|
||||
{"config-file", 1, 0, 'c'},
|
||||
{"disable-color", 0, 0, 's'},
|
||||
{"timestamp", 0, 0, 'T'},
|
||||
{"msc", 1, 0, 'm'},
|
||||
{"local", 1, 0, 'l'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
c = getopt_long(argc, argv, "hd:sTPc:m:l:",
|
||||
long_options, &option_index);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_usage();
|
||||
print_help();
|
||||
exit(0);
|
||||
case 's':
|
||||
log_set_use_color(stderr_target, 0);
|
||||
break;
|
||||
case 'd':
|
||||
log_parse_category_mask(stderr_target, optarg);
|
||||
break;
|
||||
case 'c':
|
||||
config_file = strdup(optarg);
|
||||
break;
|
||||
case 'T':
|
||||
log_set_print_timestamp(stderr_target, 1);
|
||||
break;
|
||||
case 'm':
|
||||
msc_ip = optarg;
|
||||
break;
|
||||
case 'l':
|
||||
inet_aton(optarg, &local_addr);
|
||||
break;
|
||||
default:
|
||||
/* ignore */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void signal_handler(int signal)
|
||||
{
|
||||
switch (signal) {
|
||||
case SIGABRT:
|
||||
/* in case of abort, we want to obtain a talloc report
|
||||
* and then return to the caller, who will abort the process */
|
||||
case SIGUSR1:
|
||||
talloc_report_full(tall_bsc_ctx, stderr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern void *tall_msgb_ctx;
|
||||
extern void *tall_ctr_ctx;
|
||||
static void talloc_init_ctx()
|
||||
{
|
||||
tall_bsc_ctx = talloc_named_const(NULL, 0, "nat");
|
||||
tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb");
|
||||
tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
talloc_init_ctx();
|
||||
log_init(&log_info);
|
||||
|
||||
stderr_target = log_target_create_stderr();
|
||||
log_add_target(stderr_target);
|
||||
log_set_all_filter(stderr_target, 1);
|
||||
|
||||
nat = bsc_nat_alloc();
|
||||
if (!nat) {
|
||||
fprintf(stderr, "Failed to allocate the BSC nat.\n");
|
||||
return -4;
|
||||
}
|
||||
|
||||
nat->mgcp_cfg = talloc_zero(nat, struct mgcp_config);
|
||||
if (!nat->mgcp_cfg) {
|
||||
fprintf(stderr, "Failed to allocate MGCP cfg.\n");
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* parse options */
|
||||
local_addr.s_addr = INADDR_ANY;
|
||||
handle_options(argc, argv);
|
||||
|
||||
/* init vty and parse */
|
||||
bsc_nat_vty_init(nat);
|
||||
telnet_init(NULL, 4244);
|
||||
if (mgcp_parse_config(config_file, nat->mgcp_cfg) < 0) {
|
||||
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* over rule the VTY config */
|
||||
if (msc_ip)
|
||||
bsc_nat_set_msc_ip(nat, msc_ip);
|
||||
|
||||
/* seed the PRNG */
|
||||
srand(time(NULL));
|
||||
|
||||
/*
|
||||
* Setup the MGCP code..
|
||||
*/
|
||||
if (bsc_mgcp_nat_init(nat) != 0)
|
||||
return -4;
|
||||
|
||||
/* connect to the MSC */
|
||||
nat->msc_con = bsc_msc_create(nat->msc_ip, nat->msc_port);
|
||||
if (!nat->msc_con) {
|
||||
fprintf(stderr, "Creating a bsc_msc_connection failed.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
nat->msc_con->connection_loss = msc_connection_was_lost;
|
||||
nat->msc_con->write_queue.read_cb = ipaccess_msc_read_cb;
|
||||
nat->msc_con->write_queue.write_cb = ipaccess_msc_write_cb;;
|
||||
bsc_msc_connect(nat->msc_con);
|
||||
|
||||
/* wait for the BSC */
|
||||
if (listen_for_bsc(&bsc_listen, &local_addr, 5000) < 0) {
|
||||
fprintf(stderr, "Failed to listen for BSC.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
signal(SIGABRT, &signal_handler);
|
||||
signal(SIGUSR1, &signal_handler);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
while (1) {
|
||||
bsc_select_main(0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
195
openbsc/src/nat/bsc_nat_utils.c
Normal file
195
openbsc/src/nat/bsc_nat_utils.c
Normal file
@@ -0,0 +1,195 @@
|
||||
|
||||
/* BSC Multiplexer/NAT Utilities */
|
||||
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openbsc/bsc_nat.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/bssap.h>
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/ipaccess.h>
|
||||
|
||||
#include <osmocore/linuxlist.h>
|
||||
#include <osmocore/talloc.h>
|
||||
#include <osmocore/gsm0808.h>
|
||||
|
||||
#include <sccp/sccp.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
struct bsc_nat *bsc_nat_alloc(void)
|
||||
{
|
||||
struct bsc_nat *nat = talloc_zero(tall_bsc_ctx, struct bsc_nat);
|
||||
if (!nat)
|
||||
return NULL;
|
||||
|
||||
INIT_LLIST_HEAD(&nat->sccp_connections);
|
||||
INIT_LLIST_HEAD(&nat->bsc_connections);
|
||||
INIT_LLIST_HEAD(&nat->bsc_configs);
|
||||
nat->stats.sccp.conn = counter_alloc("nat.sccp.conn");
|
||||
nat->stats.sccp.calls = counter_alloc("nat.sccp.calls");
|
||||
nat->stats.bsc.reconn = counter_alloc("nat.bsc.conn");
|
||||
nat->stats.bsc.auth_fail = counter_alloc("nat.bsc.auth_fail");
|
||||
nat->stats.msc.reconn = counter_alloc("nat.msc.conn");
|
||||
nat->msc_ip = talloc_strdup(nat, "127.0.0.1");
|
||||
nat->msc_port = 5000;
|
||||
nat->auth_timeout = 2;
|
||||
nat->ping_timeout = 20;
|
||||
nat->pong_timeout = 5;
|
||||
return nat;
|
||||
}
|
||||
|
||||
void bsc_nat_set_msc_ip(struct bsc_nat *nat, const char *ip)
|
||||
{
|
||||
if (nat->msc_ip)
|
||||
talloc_free(nat->msc_ip);
|
||||
nat->msc_ip = talloc_strdup(nat, ip);
|
||||
}
|
||||
|
||||
struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat)
|
||||
{
|
||||
struct bsc_connection *con = talloc_zero(nat, struct bsc_connection);
|
||||
if (!con)
|
||||
return NULL;
|
||||
|
||||
con->nat = nat;
|
||||
write_queue_init(&con->write_queue, 100);
|
||||
return con;
|
||||
}
|
||||
|
||||
struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token, unsigned int lac)
|
||||
{
|
||||
struct bsc_config *conf = talloc_zero(nat, struct bsc_config);
|
||||
if (!conf)
|
||||
return NULL;
|
||||
|
||||
conf->token = talloc_strdup(conf, token);
|
||||
conf->lac = lac;
|
||||
conf->nr = nat->num_bsc;
|
||||
conf->nat = nat;
|
||||
|
||||
llist_add_tail(&conf->entry, &nat->bsc_configs);
|
||||
++nat->num_bsc;
|
||||
|
||||
conf->stats.sccp.conn = counter_alloc("nat.bsc.sccp.conn");
|
||||
conf->stats.sccp.calls = counter_alloc("nat.bsc.sccp.calls");
|
||||
conf->stats.net.reconn = counter_alloc("nat.bsc.net.reconnects");
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
void sccp_connection_destroy(struct sccp_connections *conn)
|
||||
{
|
||||
LOGP(DNAT, LOGL_DEBUG, "Destroy 0x%x <-> 0x%x mapping for con %p\n",
|
||||
sccp_src_ref_to_int(&conn->real_ref),
|
||||
sccp_src_ref_to_int(&conn->patched_ref), conn->bsc);
|
||||
bsc_mgcp_dlcx(conn);
|
||||
llist_del(&conn->list_entry);
|
||||
talloc_free(conn);
|
||||
}
|
||||
|
||||
struct bsc_connection *bsc_nat_find_bsc(struct bsc_nat *nat, struct msgb *msg, int *lac_out)
|
||||
{
|
||||
struct bsc_connection *bsc;
|
||||
int data_length;
|
||||
const u_int8_t *data;
|
||||
struct tlv_parsed tp;
|
||||
int i = 0;
|
||||
|
||||
*lac_out = -1;
|
||||
|
||||
if (!msg->l3h || msgb_l3len(msg) < 3) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Paging message is too short.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0);
|
||||
if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) {
|
||||
LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
|
||||
data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
|
||||
|
||||
/* No need to try a different BSS */
|
||||
if (data[0] == CELL_IDENT_BSS) {
|
||||
return NULL;
|
||||
} else if (data[0] != CELL_IDENT_LAC) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Unhandled cell ident discrminator: %d\n", data[0]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Currently we only handle one BSC */
|
||||
for (i = 1; i < data_length - 1; i += 2) {
|
||||
unsigned int _lac = ntohs(*(unsigned int *) &data[i]);
|
||||
*lac_out = _lac;
|
||||
llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) {
|
||||
if (!bsc->cfg)
|
||||
continue;
|
||||
if (!bsc->authenticated || _lac != bsc->cfg->lac)
|
||||
continue;
|
||||
|
||||
return bsc;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bsc_write_mgcp(struct bsc_connection *bsc, const u_int8_t *data, unsigned int length)
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
if (length > 4096 - 128) {
|
||||
LOGP(DINP, LOGL_ERROR, "Can not send message of that size.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg = msgb_alloc_headroom(4096, 128, "to-bsc");
|
||||
if (!msg) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy the data */
|
||||
msg->l3h = msgb_put(msg, length);
|
||||
memcpy(msg->l3h, data, length);
|
||||
|
||||
return bsc_write(bsc, msg, NAT_IPAC_PROTO_MGCP);
|
||||
}
|
||||
|
||||
int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int proto)
|
||||
{
|
||||
/* prepend the header */
|
||||
ipaccess_prepend_header(msg, proto);
|
||||
|
||||
if (write_queue_enqueue(&bsc->write_queue, msg) != 0) {
|
||||
LOGP(DINP, LOGL_ERROR, "Failed to enqueue the write.\n");
|
||||
msgb_free(msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
460
openbsc/src/nat/bsc_nat_vty.c
Normal file
460
openbsc/src/nat/bsc_nat_vty.c
Normal file
@@ -0,0 +1,460 @@
|
||||
/* OpenBSC NAT interface to quagga VTY */
|
||||
/* (C) 2010 by Holger Hans Peter Freyther
|
||||
* (C) 2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <vty/command.h>
|
||||
#include <vty/buffer.h>
|
||||
#include <vty/vty.h>
|
||||
|
||||
#include <openbsc/bsc_nat.h>
|
||||
#include <openbsc/bsc_msc.h>
|
||||
#include <openbsc/gsm_04_08.h>
|
||||
#include <openbsc/mgcp.h>
|
||||
#include <openbsc/vty.h>
|
||||
|
||||
#include <osmocore/talloc.h>
|
||||
|
||||
#include <sccp/sccp.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static struct bsc_nat *_nat;
|
||||
|
||||
static struct cmd_node nat_node = {
|
||||
NAT_NODE,
|
||||
"%s(nat)#",
|
||||
1,
|
||||
};
|
||||
|
||||
static struct cmd_node bsc_node = {
|
||||
BSC_NODE,
|
||||
"%s(bsc)#",
|
||||
1,
|
||||
};
|
||||
|
||||
static int config_write_nat(struct vty *vty)
|
||||
{
|
||||
vty_out(vty, "nat%s", VTY_NEWLINE);
|
||||
if (_nat->imsi_allow)
|
||||
vty_out(vty, " imsi allow %s%s", _nat->imsi_allow, VTY_NEWLINE);
|
||||
if (_nat->imsi_deny)
|
||||
vty_out(vty, " insi deny %s%s", _nat->imsi_deny, VTY_NEWLINE);
|
||||
vty_out(vty, " msc ip %s%s", _nat->msc_ip, VTY_NEWLINE);
|
||||
vty_out(vty, " msc port %d%s", _nat->msc_port, VTY_NEWLINE);
|
||||
vty_out(vty, " timeout auth %d%s", _nat->auth_timeout, VTY_NEWLINE);
|
||||
vty_out(vty, " timeout ping %d%s", _nat->ping_timeout, VTY_NEWLINE);
|
||||
vty_out(vty, " timeout pong %d%s", _nat->pong_timeout, VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void config_write_bsc_single(struct vty *vty, struct bsc_config *bsc)
|
||||
{
|
||||
vty_out(vty, " bsc %u%s", bsc->nr, VTY_NEWLINE);
|
||||
vty_out(vty, " token %s%s", bsc->token, VTY_NEWLINE);
|
||||
vty_out(vty, " location_area_code %u%s", bsc->lac, VTY_NEWLINE);
|
||||
if (bsc->imsi_allow)
|
||||
vty_out(vty, " imsi allow %s%s", bsc->imsi_allow, VTY_NEWLINE);
|
||||
if (bsc->imsi_deny)
|
||||
vty_out(vty, " imsi deny %s%s", bsc->imsi_deny, VTY_NEWLINE);
|
||||
vty_out(vty, " paging forbidden %d%s", bsc->forbid_paging, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
static int config_write_bsc(struct vty *vty)
|
||||
{
|
||||
struct bsc_config *bsc;
|
||||
|
||||
llist_for_each_entry(bsc, &_nat->bsc_configs, entry)
|
||||
config_write_bsc_single(vty, bsc);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(show_sccp, show_sccp_cmd, "show sccp connections",
|
||||
SHOW_STR "Display information about current SCCP connections")
|
||||
{
|
||||
struct sccp_connections *con;
|
||||
vty_out(vty, "Listing all opening SCCP connections%s", VTY_NEWLINE);
|
||||
|
||||
llist_for_each_entry(con, &_nat->sccp_connections, list_entry) {
|
||||
vty_out(vty, "For BSC Nr: %d lac: %d; BSC ref: 0x%x; MUX ref: 0x%x; Network has ref: %d ref: 0x%x MSC/BSC mux: 0x%x/0x%x%s",
|
||||
con->bsc->cfg ? con->bsc->cfg->nr : -1,
|
||||
con->bsc->cfg ? con->bsc->cfg->lac : -1,
|
||||
sccp_src_ref_to_int(&con->real_ref),
|
||||
sccp_src_ref_to_int(&con->patched_ref),
|
||||
con->has_remote_ref,
|
||||
sccp_src_ref_to_int(&con->remote_ref),
|
||||
con->msc_timeslot, con->bsc_timeslot,
|
||||
VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_bsc, show_bsc_cmd, "show bsc connections",
|
||||
SHOW_STR "Display information about current BSCs")
|
||||
{
|
||||
struct bsc_connection *con;
|
||||
struct sockaddr_in sock;
|
||||
socklen_t len = sizeof(sock);
|
||||
|
||||
llist_for_each_entry(con, &_nat->bsc_connections, list_entry) {
|
||||
getpeername(con->write_queue.bfd.fd, (struct sockaddr *) &sock, &len);
|
||||
vty_out(vty, "BSC nr: %d lac: %d auth: %d fd: %d peername: %s%s",
|
||||
con->cfg ? con->cfg->nr : -1,
|
||||
con->cfg ? con->cfg->lac : -1,
|
||||
con->authenticated, con->write_queue.bfd.fd,
|
||||
inet_ntoa(sock.sin_addr), VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_bsc_cfg, show_bsc_cfg_cmd, "show bsc config",
|
||||
SHOW_STR "Display information about known BSC configs")
|
||||
{
|
||||
struct bsc_config *conf;
|
||||
llist_for_each_entry(conf, &_nat->bsc_configs, entry) {
|
||||
vty_out(vty, "BSC token: '%s' lac: %u nr: %u%s",
|
||||
conf->token, conf->lac, conf->nr, VTY_NEWLINE);
|
||||
vty_out(vty, " imsi_allow: '%s' imsi_deny: '%s'%s",
|
||||
conf->imsi_allow ? conf->imsi_allow: "any",
|
||||
conf->imsi_deny ? conf->imsi_deny : "none",
|
||||
VTY_NEWLINE);
|
||||
vty_out(vty, " paging forbidden: %d%s",
|
||||
conf->forbid_paging, VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_stats,
|
||||
show_stats_cmd,
|
||||
"show statistics [NR]",
|
||||
SHOW_STR "Display network statistics")
|
||||
{
|
||||
struct bsc_config *conf;
|
||||
|
||||
int nr = -1;
|
||||
|
||||
if (argc == 1)
|
||||
nr = atoi(argv[0]);
|
||||
|
||||
vty_out(vty, "NAT statistics%s", VTY_NEWLINE);
|
||||
vty_out(vty, " SCCP Connections %lu total, %lu calls%s",
|
||||
counter_get(_nat->stats.sccp.conn),
|
||||
counter_get(_nat->stats.sccp.calls), VTY_NEWLINE);
|
||||
vty_out(vty, " MSC Connections %lu%s",
|
||||
counter_get(_nat->stats.msc.reconn), VTY_NEWLINE);
|
||||
vty_out(vty, " BSC Connections %lu total, %lu auth failed.%s",
|
||||
counter_get(_nat->stats.bsc.reconn),
|
||||
counter_get(_nat->stats.bsc.auth_fail), VTY_NEWLINE);
|
||||
|
||||
llist_for_each_entry(conf, &_nat->bsc_configs, entry) {
|
||||
if (argc == 1 && nr != conf->nr)
|
||||
continue;
|
||||
|
||||
vty_out(vty, " BSC lac: %d nr: %d%s",
|
||||
conf->lac, conf->nr, VTY_NEWLINE);
|
||||
vty_out(vty, " SCCP Connnections %lu total, %lu calls%s",
|
||||
counter_get(conf->stats.sccp.conn),
|
||||
counter_get(conf->stats.sccp.calls), VTY_NEWLINE);
|
||||
vty_out(vty, " BSC Connections %lu total%s",
|
||||
counter_get(conf->stats.net.reconn), VTY_NEWLINE);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(show_msc,
|
||||
show_msc_cmd,
|
||||
"show msc connection",
|
||||
SHOW_STR "Show the status of the MSC connection.")
|
||||
{
|
||||
if (!_nat->msc_con) {
|
||||
vty_out(vty, "The MSC is not yet configured.\n");
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vty_out(vty, "MSC on %s:%d is connected: %d%s\n",
|
||||
_nat->msc_con->ip, _nat->msc_con->port,
|
||||
_nat->msc_con->is_connected, VTY_NEWLINE);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(close_bsc,
|
||||
close_bsc_cmd,
|
||||
"close bsc connection BSC_NR",
|
||||
"Close the connection with the BSC identified by the config number.")
|
||||
{
|
||||
struct bsc_connection *bsc;
|
||||
int bsc_nr = atoi(argv[0]);
|
||||
|
||||
llist_for_each_entry(bsc, &_nat->bsc_connections, list_entry) {
|
||||
if (!bsc->cfg || bsc->cfg->nr != bsc_nr)
|
||||
continue;
|
||||
bsc_close_connection(bsc);
|
||||
break;
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_nat, cfg_nat_cmd, "nat", "Configute the NAT")
|
||||
{
|
||||
vty->index = _nat;
|
||||
vty->node = NAT_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
static void parse_reg(void *ctx, regex_t *reg, char **imsi, int argc, const char **argv)
|
||||
{
|
||||
if (*imsi) {
|
||||
talloc_free(*imsi);
|
||||
*imsi = NULL;
|
||||
}
|
||||
regfree(reg);
|
||||
|
||||
if (argc > 0) {
|
||||
*imsi = talloc_strdup(ctx, argv[0]);
|
||||
regcomp(reg, argv[0], 0);
|
||||
}
|
||||
}
|
||||
|
||||
DEFUN(cfg_nat_imsi_allow,
|
||||
cfg_nat_imsi_allow_cmd,
|
||||
"imsi allow [REGEXP]",
|
||||
"Allow matching IMSIs to talk to the MSC. "
|
||||
"The defualt is to allow everyone.")
|
||||
{
|
||||
parse_reg(_nat, &_nat->imsi_allow_re, &_nat->imsi_allow, argc, argv);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_nat_imsi_deny,
|
||||
cfg_nat_imsi_deny_cmd,
|
||||
"imsi deny [REGEXP]",
|
||||
"Deny matching IMSIs to talk to the MSC. "
|
||||
"The defualt is to not deny.")
|
||||
{
|
||||
parse_reg(_nat, &_nat->imsi_deny_re, &_nat->imsi_deny, argc, argv);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_nat_msc_ip,
|
||||
cfg_nat_msc_ip_cmd,
|
||||
"msc ip IP",
|
||||
"Set the IP address of the MSC.")
|
||||
{
|
||||
bsc_nat_set_msc_ip(_nat, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_nat_msc_port,
|
||||
cfg_nat_msc_port_cmd,
|
||||
"msc port <1-65500>",
|
||||
"Set the port of the MSC.")
|
||||
{
|
||||
_nat->msc_port = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_nat_auth_time,
|
||||
cfg_nat_auth_time_cmd,
|
||||
"timeout auth <1-256>",
|
||||
"The time to wait for an auth response.")
|
||||
{
|
||||
_nat->auth_timeout = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_nat_ping_time,
|
||||
cfg_nat_ping_time_cmd,
|
||||
"timeout ping NR",
|
||||
"Send a ping every NR seconds. Negative to disable.")
|
||||
{
|
||||
_nat->ping_timeout = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_nat_pong_time,
|
||||
cfg_nat_pong_time_cmd,
|
||||
"timeout pong NR",
|
||||
"Wait NR seconds for the PONG response. Should be smaller than ping.")
|
||||
{
|
||||
_nat->pong_timeout = atoi(argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* per BSC configuration */
|
||||
DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR", "Select a BSC to configure")
|
||||
{
|
||||
int bsc_nr = atoi(argv[0]);
|
||||
struct bsc_config *bsc;
|
||||
|
||||
if (bsc_nr > _nat->num_bsc) {
|
||||
vty_out(vty, "%% The next unused BSC number is %u%s",
|
||||
_nat->num_bsc, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
} else if (bsc_nr == _nat->num_bsc) {
|
||||
/* allocate a new one */
|
||||
bsc = bsc_config_alloc(_nat, "unknown", 0);
|
||||
} else
|
||||
bsc = bsc_config_num(_nat, bsc_nr);
|
||||
|
||||
if (!bsc)
|
||||
return CMD_WARNING;
|
||||
|
||||
vty->index = bsc;
|
||||
vty->node = BSC_NODE;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bsc_token, cfg_bsc_token_cmd, "token TOKEN", "Set the token")
|
||||
{
|
||||
struct bsc_config *conf = vty->index;
|
||||
|
||||
if (conf->token)
|
||||
talloc_free(conf->token);
|
||||
conf->token = talloc_strdup(conf, argv[0]);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bsc_lac, cfg_bsc_lac_cmd, "location_area_code <0-65535>",
|
||||
"Set the Location Area Code (LAC) of this BSC")
|
||||
{
|
||||
struct bsc_config *tmp;
|
||||
struct bsc_config *conf = vty->index;
|
||||
|
||||
int lac = atoi(argv[0]);
|
||||
|
||||
if (lac < 0 || lac > 0xffff) {
|
||||
vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s",
|
||||
lac, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) {
|
||||
vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s",
|
||||
lac, VTY_NEWLINE);
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
/* verify that the LACs are unique */
|
||||
llist_for_each_entry(tmp, &_nat->bsc_configs, entry) {
|
||||
if (tmp->lac == lac) {
|
||||
vty_out(vty, "%% LAC %d is already used.%s", lac, VTY_NEWLINE);
|
||||
return CMD_ERR_INCOMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
conf->lac = lac;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bsc_imsi_allow,
|
||||
cfg_bsc_imsi_allow_cmd,
|
||||
"imsi allow [REGEXP]",
|
||||
"Allow IMSIs with the following network to talk to the MSC."
|
||||
"The default is to allow everyone)")
|
||||
{
|
||||
struct bsc_config *conf = vty->index;
|
||||
|
||||
parse_reg(conf, &conf->imsi_allow_re, &conf->imsi_allow, argc, argv);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bsc_imsi_deny,
|
||||
cfg_bsc_imsi_deny_cmd,
|
||||
"imsi deny [REGEXP]",
|
||||
"Deny IMSIs with the following network to talk to the MSC."
|
||||
"The default is to not deny anyone.)")
|
||||
{
|
||||
struct bsc_config *conf = vty->index;
|
||||
|
||||
parse_reg(conf, &conf->imsi_deny_re, &conf->imsi_deny, argc, argv);
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_bsc_paging,
|
||||
cfg_bsc_paging_cmd,
|
||||
"paging forbidden (0|1)",
|
||||
"Forbid sending PAGING REQUESTS to the BSC.")
|
||||
{
|
||||
struct bsc_config *conf = vty->index;
|
||||
|
||||
if (strcmp("1", argv[0]) == 0)
|
||||
conf->forbid_paging = 1;
|
||||
else
|
||||
conf->forbid_paging = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
int bsc_nat_vty_init(struct bsc_nat *nat)
|
||||
{
|
||||
_nat = nat;
|
||||
|
||||
cmd_init(1);
|
||||
vty_init();
|
||||
|
||||
/* show commands */
|
||||
install_element(VIEW_NODE, &show_sccp_cmd);
|
||||
install_element(VIEW_NODE, &show_bsc_cmd);
|
||||
install_element(VIEW_NODE, &show_bsc_cfg_cmd);
|
||||
install_element(VIEW_NODE, &show_stats_cmd);
|
||||
install_element(VIEW_NODE, &close_bsc_cmd);
|
||||
install_element(VIEW_NODE, &show_msc_cmd);
|
||||
|
||||
openbsc_vty_add_cmds();
|
||||
|
||||
/* nat group */
|
||||
install_element(CONFIG_NODE, &cfg_nat_cmd);
|
||||
install_node(&nat_node, config_write_nat);
|
||||
install_default(NAT_NODE);
|
||||
install_element(NAT_NODE, &cfg_nat_imsi_allow_cmd);
|
||||
install_element(NAT_NODE, &cfg_nat_imsi_deny_cmd);
|
||||
install_element(NAT_NODE, &cfg_nat_msc_ip_cmd);
|
||||
install_element(NAT_NODE, &cfg_nat_msc_port_cmd);
|
||||
install_element(NAT_NODE, &cfg_nat_auth_time_cmd);
|
||||
install_element(NAT_NODE, &cfg_nat_ping_time_cmd);
|
||||
install_element(NAT_NODE, &cfg_nat_pong_time_cmd);
|
||||
|
||||
/* BSC subgroups */
|
||||
install_element(NAT_NODE, &cfg_bsc_cmd);
|
||||
install_node(&bsc_node, config_write_bsc);
|
||||
install_default(BSC_NODE);
|
||||
install_element(BSC_NODE, &cfg_bsc_token_cmd);
|
||||
install_element(BSC_NODE, &cfg_bsc_lac_cmd);
|
||||
install_element(BSC_NODE, &cfg_bsc_imsi_allow_cmd);
|
||||
install_element(BSC_NODE, &cfg_bsc_imsi_deny_cmd);
|
||||
install_element(BSC_NODE, &cfg_bsc_paging_cmd);
|
||||
|
||||
mgcp_vty_init();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* called by the telnet interface... we have our own init above */
|
||||
void bsc_vty_init()
|
||||
{}
|
||||
232
openbsc/src/nat/bsc_sccp.c
Normal file
232
openbsc/src/nat/bsc_sccp.c
Normal file
@@ -0,0 +1,232 @@
|
||||
/* SCCP patching and handling routines */
|
||||
/*
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <openbsc/debug.h>
|
||||
#include <openbsc/bsc_nat.h>
|
||||
|
||||
#include <sccp/sccp.h>
|
||||
|
||||
#include <osmocore/talloc.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static int equal(struct sccp_source_reference *ref1, struct sccp_source_reference *ref2)
|
||||
{
|
||||
return memcmp(ref1, ref2, sizeof(*ref1)) == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SCCP patching below
|
||||
*/
|
||||
|
||||
/* check if we are using this ref for patched already */
|
||||
static int sccp_ref_is_free(struct sccp_source_reference *ref, struct bsc_nat *nat)
|
||||
{
|
||||
struct sccp_connections *conn;
|
||||
|
||||
llist_for_each_entry(conn, &nat->sccp_connections, list_entry) {
|
||||
if (memcmp(ref, &conn->patched_ref, sizeof(*ref)) == 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* copied from sccp.c */
|
||||
static int assign_src_local_reference(struct sccp_source_reference *ref, struct bsc_nat *nat)
|
||||
{
|
||||
static u_int32_t last_ref = 0x50000;
|
||||
int wrapped = 0;
|
||||
|
||||
do {
|
||||
struct sccp_source_reference reference;
|
||||
reference.octet1 = (last_ref >> 0) & 0xff;
|
||||
reference.octet2 = (last_ref >> 8) & 0xff;
|
||||
reference.octet3 = (last_ref >> 16) & 0xff;
|
||||
|
||||
++last_ref;
|
||||
/* do not use the reversed word and wrap around */
|
||||
if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) {
|
||||
LOGP(DNAT, LOGL_NOTICE, "Wrapped searching for a free code\n");
|
||||
last_ref = 0;
|
||||
++wrapped;
|
||||
}
|
||||
|
||||
if (sccp_ref_is_free(&reference, nat) == 0) {
|
||||
*ref = reference;
|
||||
return 0;
|
||||
}
|
||||
} while (wrapped != 2);
|
||||
|
||||
LOGP(DNAT, LOGL_ERROR, "Finding a free reference failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int create_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed)
|
||||
{
|
||||
struct sccp_connections *conn;
|
||||
|
||||
/* Some commercial BSCs like to reassign there SRC ref */
|
||||
llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
|
||||
if (conn->bsc != bsc)
|
||||
continue;
|
||||
if (memcmp(&conn->real_ref, parsed->src_local_ref, sizeof(conn->real_ref)) != 0)
|
||||
continue;
|
||||
|
||||
/* the BSC has reassigned the SRC ref and we failed to keep track */
|
||||
memset(&conn->remote_ref, 0, sizeof(conn->remote_ref));
|
||||
if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) {
|
||||
LOGP(DNAT, LOGL_ERROR, "BSC %d reused src ref: %d and we failed to generate a new id.\n",
|
||||
bsc->cfg->nr, sccp_src_ref_to_int(parsed->src_local_ref));
|
||||
llist_del(&conn->list_entry);
|
||||
talloc_free(conn);
|
||||
return -1;
|
||||
} else {
|
||||
bsc_mgcp_dlcx(conn);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
conn = talloc_zero(bsc->nat, struct sccp_connections);
|
||||
if (!conn) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
conn->bsc = bsc;
|
||||
conn->real_ref = *parsed->src_local_ref;
|
||||
if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) {
|
||||
LOGP(DNAT, LOGL_ERROR, "Failed to assign a ref.\n");
|
||||
talloc_free(conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bsc_mgcp_init(conn);
|
||||
llist_add_tail(&conn->list_entry, &bsc->nat->sccp_connections);
|
||||
counter_inc(bsc->cfg->stats.sccp.conn);
|
||||
counter_inc(bsc->cfg->nat->stats.sccp.conn);
|
||||
|
||||
LOGP(DNAT, LOGL_DEBUG, "Created 0x%x <-> 0x%x mapping for con %p\n",
|
||||
sccp_src_ref_to_int(&conn->real_ref),
|
||||
sccp_src_ref_to_int(&conn->patched_ref), bsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int update_sccp_src_ref(struct sccp_connections *sccp, struct bsc_nat_parsed *parsed)
|
||||
{
|
||||
if (!parsed->dest_local_ref || !parsed->src_local_ref) {
|
||||
LOGP(DNAT, LOGL_ERROR, "CC MSG should contain both local and dest address.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sccp->remote_ref = *parsed->src_local_ref;
|
||||
sccp->has_remote_ref = 1;
|
||||
LOGP(DNAT, LOGL_DEBUG, "Updating 0x%x to remote 0x%x on %p\n",
|
||||
sccp_src_ref_to_int(&sccp->patched_ref),
|
||||
sccp_src_ref_to_int(&sccp->remote_ref), sccp->bsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed)
|
||||
{
|
||||
struct sccp_connections *conn;
|
||||
|
||||
llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
|
||||
if (memcmp(parsed->src_local_ref,
|
||||
&conn->patched_ref, sizeof(conn->patched_ref)) == 0) {
|
||||
|
||||
sccp_connection_destroy(conn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOGP(DNAT, LOGL_ERROR, "Can not remove connection: 0x%x\n",
|
||||
sccp_src_ref_to_int(parsed->src_local_ref));
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a message from the MSC to the BSC. The MSC is using
|
||||
* an address that was assigned by the MUX, we need to update the
|
||||
* dest reference to the real network.
|
||||
*/
|
||||
struct sccp_connections *patch_sccp_src_ref_to_bsc(struct msgb *msg,
|
||||
struct bsc_nat_parsed *parsed,
|
||||
struct bsc_nat *nat)
|
||||
{
|
||||
struct sccp_connections *conn;
|
||||
|
||||
if (!parsed->dest_local_ref) {
|
||||
LOGP(DNAT, LOGL_ERROR, "MSG should contain dest_local_ref.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
llist_for_each_entry(conn, &nat->sccp_connections, list_entry) {
|
||||
if (!equal(parsed->dest_local_ref, &conn->patched_ref))
|
||||
continue;
|
||||
|
||||
/* Change the dest address to the real one */
|
||||
*parsed->dest_local_ref = conn->real_ref;
|
||||
return conn;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* These are message to the MSC. We will need to find the BSC
|
||||
* Connection by either the SRC or the DST local reference.
|
||||
*
|
||||
* In case of a CR we need to work by the SRC local reference
|
||||
* in all other cases we need to work by the destination local
|
||||
* reference..
|
||||
*/
|
||||
struct sccp_connections *patch_sccp_src_ref_to_msc(struct msgb *msg,
|
||||
struct bsc_nat_parsed *parsed,
|
||||
struct bsc_connection *bsc)
|
||||
{
|
||||
struct sccp_connections *conn;
|
||||
|
||||
llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) {
|
||||
if (conn->bsc != bsc)
|
||||
continue;
|
||||
|
||||
if (parsed->src_local_ref) {
|
||||
if (equal(parsed->src_local_ref, &conn->real_ref)) {
|
||||
*parsed->src_local_ref = conn->patched_ref;
|
||||
return conn;
|
||||
}
|
||||
} else if (parsed->dest_local_ref) {
|
||||
if (equal(parsed->dest_local_ref, &conn->remote_ref))
|
||||
return conn;
|
||||
} else {
|
||||
LOGP(DNAT, LOGL_ERROR, "Header has neither loc/dst ref.\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
!
|
||||
! OpenBSC configuration saved from vty
|
||||
!
|
||||
! !
|
||||
password foo
|
||||
!
|
||||
line vty
|
||||
@@ -11,17 +11,52 @@ network
|
||||
mobile network code 1
|
||||
short name OpenBSC
|
||||
long name OpenBSC
|
||||
auth policy closed
|
||||
location updating reject cause 13
|
||||
encryption a5 0
|
||||
neci 0
|
||||
rrlp mode none
|
||||
mm info 1
|
||||
handover 0
|
||||
handover window rxlev averaging 10
|
||||
handover window rxqual averaging 1
|
||||
handover window rxlev neighbor averaging 10
|
||||
handover power budget interval 6
|
||||
handover power budget hysteresis 3
|
||||
handover maximum distance 9999
|
||||
timer t3101 10
|
||||
timer t3103 0
|
||||
timer t3105 0
|
||||
timer t3107 0
|
||||
timer t3109 0
|
||||
timer t3111 0
|
||||
timer t3113 60
|
||||
timer t3115 0
|
||||
timer t3117 0
|
||||
timer t3119 0
|
||||
timer t3141 0
|
||||
bts 0
|
||||
type nanobts
|
||||
ip.access unit_id 1801 0
|
||||
band GSM1800
|
||||
band DCS1800
|
||||
cell_identity 0
|
||||
location_area_code 1
|
||||
training_sequence_code 7
|
||||
base_station_id_code 63
|
||||
ms max power 15
|
||||
cell reselection hysteresis 4
|
||||
rxlev access min 0
|
||||
channel allocator ascending
|
||||
rach tx integer 9
|
||||
rach max transmission 7
|
||||
ip.access unit_id 1801 0
|
||||
oml ip.access stream_id 255
|
||||
gprs mode none
|
||||
trx 0
|
||||
rf_locked 0
|
||||
arfcn 514
|
||||
nominal power 23
|
||||
max_power_red 20
|
||||
rsl e1 tei 0
|
||||
timeslot 0
|
||||
phys_chan_config CCCH+SDCCH4
|
||||
timeslot 1
|
||||
|
||||
97
openbsc/src/openbsc.cfg.nanobts.multitrx
Normal file
97
openbsc/src/openbsc.cfg.nanobts.multitrx
Normal file
@@ -0,0 +1,97 @@
|
||||
!
|
||||
! OpenBSC configuration saved from vty
|
||||
! !
|
||||
password foo
|
||||
!
|
||||
line vty
|
||||
no login
|
||||
!
|
||||
network
|
||||
network country code 1
|
||||
mobile network code 1
|
||||
short name OpenBSC
|
||||
long name OpenBSC
|
||||
auth policy closed
|
||||
location updating reject cause 13
|
||||
encryption a5 0
|
||||
neci 0
|
||||
rrlp mode none
|
||||
mm info 0
|
||||
handover 0
|
||||
handover window rxlev averaging 10
|
||||
handover window rxqual averaging 1
|
||||
handover window rxlev neighbor averaging 10
|
||||
handover power budget interval 6
|
||||
handover power budget hysteresis 3
|
||||
handover maximum distance 9999
|
||||
timer t3101 10
|
||||
timer t3103 0
|
||||
timer t3105 0
|
||||
timer t3107 0
|
||||
timer t3109 0
|
||||
timer t3111 0
|
||||
timer t3113 60
|
||||
timer t3115 0
|
||||
timer t3117 0
|
||||
timer t3119 0
|
||||
timer t3141 0
|
||||
bts 0
|
||||
type nanobts
|
||||
band DCS1800
|
||||
cell_identity 0
|
||||
location_area_code 1
|
||||
training_sequence_code 7
|
||||
base_station_id_code 63
|
||||
ms max power 15
|
||||
cell reselection hysteresis 4
|
||||
rxlev access min 0
|
||||
channel allocator ascending
|
||||
rach tx integer 9
|
||||
rach max transmission 7
|
||||
ip.access unit_id 1800 0
|
||||
oml ip.access stream_id 255
|
||||
gprs mode none
|
||||
trx 0
|
||||
rf_locked 0
|
||||
arfcn 871
|
||||
nominal power 23
|
||||
max_power_red 0
|
||||
rsl e1 tei 0
|
||||
timeslot 0
|
||||
phys_chan_config CCCH+SDCCH4
|
||||
timeslot 1
|
||||
phys_chan_config SDCCH8
|
||||
timeslot 2
|
||||
phys_chan_config TCH/F
|
||||
timeslot 3
|
||||
phys_chan_config TCH/F
|
||||
timeslot 4
|
||||
phys_chan_config TCH/F
|
||||
timeslot 5
|
||||
phys_chan_config TCH/F
|
||||
timeslot 6
|
||||
phys_chan_config TCH/F
|
||||
timeslot 7
|
||||
phys_chan_config TCH/F
|
||||
trx 1
|
||||
rf_locked 0
|
||||
arfcn 873
|
||||
nominal power 23
|
||||
max_power_red 0
|
||||
rsl e1 tei 0
|
||||
timeslot 0
|
||||
phys_chan_config CCCH+SDCCH4
|
||||
timeslot 1
|
||||
phys_chan_config SDCCH8
|
||||
timeslot 2
|
||||
phys_chan_config TCH/F
|
||||
timeslot 3
|
||||
phys_chan_config TCH/F
|
||||
timeslot 4
|
||||
phys_chan_config TCH/F
|
||||
timeslot 5
|
||||
phys_chan_config TCH/F
|
||||
timeslot 6
|
||||
phys_chan_config TCH/F
|
||||
timeslot 7
|
||||
phys_chan_config TCH/F
|
||||
@@ -45,9 +45,12 @@
|
||||
#include <openbsc/signal.h>
|
||||
#include <openbsc/abis_rsl.h>
|
||||
#include <openbsc/gsm_data.h>
|
||||
#include <openbsc/chan_alloc.h>
|
||||
|
||||
void *tall_paging_ctx;
|
||||
|
||||
#define PAGING_TIMER 0, 500000
|
||||
|
||||
static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *subscr)
|
||||
{
|
||||
int ccch_conf;
|
||||
@@ -70,14 +73,6 @@ static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *
|
||||
static void paging_remove_request(struct gsm_bts_paging_state *paging_bts,
|
||||
struct gsm_paging_request *to_be_deleted)
|
||||
{
|
||||
/* Update the last_request if that is necessary */
|
||||
if (to_be_deleted == paging_bts->last_request) {
|
||||
paging_bts->last_request =
|
||||
(struct gsm_paging_request *)paging_bts->last_request->entry.next;
|
||||
if (&to_be_deleted->entry == &paging_bts->pending_requests)
|
||||
paging_bts->last_request = NULL;
|
||||
}
|
||||
|
||||
bsc_del_timer(&to_be_deleted->T3113);
|
||||
llist_del(&to_be_deleted->entry);
|
||||
subscr_put(to_be_deleted->subscr);
|
||||
@@ -90,7 +85,7 @@ static void page_ms(struct gsm_paging_request *request)
|
||||
unsigned int mi_len;
|
||||
unsigned int page_group;
|
||||
|
||||
DEBUGP(DPAG, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n",
|
||||
LOGP(DPAG, LOGL_INFO, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n",
|
||||
request->subscr->imsi, request->subscr->tmsi);
|
||||
|
||||
if (request->subscr->tmsi == GSM_RESERVED_TMSI)
|
||||
@@ -103,12 +98,70 @@ static void page_ms(struct gsm_paging_request *request)
|
||||
request->chan_type);
|
||||
}
|
||||
|
||||
static void paging_move_to_next(struct gsm_bts_paging_state *paging_bts)
|
||||
static void paging_schedule_if_needed(struct gsm_bts_paging_state *paging_bts)
|
||||
{
|
||||
paging_bts->last_request =
|
||||
(struct gsm_paging_request *)paging_bts->last_request->entry.next;
|
||||
if (&paging_bts->last_request->entry == &paging_bts->pending_requests)
|
||||
paging_bts->last_request = NULL;
|
||||
if (llist_empty(&paging_bts->pending_requests))
|
||||
return;
|
||||
|
||||
if (!bsc_timer_pending(&paging_bts->work_timer))
|
||||
bsc_schedule_timer(&paging_bts->work_timer, PAGING_TIMER);
|
||||
}
|
||||
|
||||
|
||||
static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts);
|
||||
static void paging_give_credit(void *data)
|
||||
{
|
||||
struct gsm_bts_paging_state *paging_bts = data;
|
||||
|
||||
LOGP(DPAG, LOGL_NOTICE, "No slots available on bts nr %d\n", paging_bts->bts->nr);
|
||||
paging_bts->available_slots = 20;
|
||||
paging_handle_pending_requests(paging_bts);
|
||||
}
|
||||
|
||||
static int can_send_pag_req(struct gsm_bts *bts, int rsl_type)
|
||||
{
|
||||
struct pchan_load pl;
|
||||
int count;
|
||||
|
||||
memset(&pl, 0, sizeof(pl));
|
||||
bts_chan_load(&pl, bts);
|
||||
|
||||
switch (rsl_type) {
|
||||
case RSL_CHANNEED_TCH_F:
|
||||
case RSL_CHANNEED_TCH_ForH:
|
||||
goto count_tch;
|
||||
break;
|
||||
case RSL_CHANNEED_SDCCH:
|
||||
goto count_sdcch;
|
||||
break;
|
||||
case RSL_CHANNEED_ANY:
|
||||
default:
|
||||
if (bts->network->pag_any_tch)
|
||||
goto count_tch;
|
||||
else
|
||||
goto count_sdcch;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
/* could available SDCCH */
|
||||
count_sdcch:
|
||||
count = 0;
|
||||
count += pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].total
|
||||
- pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].used;
|
||||
count += pl.pchan[GSM_PCHAN_CCCH_SDCCH4].total
|
||||
- pl.pchan[GSM_PCHAN_CCCH_SDCCH4].used;
|
||||
return bts->paging.free_chans_need > count;
|
||||
|
||||
count_tch:
|
||||
count = 0;
|
||||
count += pl.pchan[GSM_PCHAN_TCH_F].total
|
||||
- pl.pchan[GSM_PCHAN_TCH_F].used;
|
||||
if (bts->network->neci)
|
||||
count += pl.pchan[GSM_PCHAN_TCH_H].total
|
||||
- pl.pchan[GSM_PCHAN_TCH_H].used;
|
||||
return bts->paging.free_chans_need > count;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -120,47 +173,49 @@ static void paging_move_to_next(struct gsm_bts_paging_state *paging_bts)
|
||||
*/
|
||||
static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts)
|
||||
{
|
||||
struct gsm_paging_request *initial_request = NULL;
|
||||
struct gsm_paging_request *current_request = NULL;
|
||||
struct gsm_paging_request *request = NULL;
|
||||
|
||||
/*
|
||||
* Determine if the pending_requests list is empty and
|
||||
* return then.
|
||||
*/
|
||||
if (llist_empty(&paging_bts->pending_requests)) {
|
||||
paging_bts->last_request = NULL;
|
||||
/* since the list is empty, no need to reschedule the timer */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!paging_bts->last_request)
|
||||
paging_bts->last_request =
|
||||
(struct gsm_paging_request *)paging_bts->pending_requests.next;
|
||||
/*
|
||||
* In case the BTS does not provide us with load indication and we
|
||||
* ran out of slots, call an autofill routine. It might be that the
|
||||
* BTS did not like our paging messages and then we have counted down
|
||||
* to zero and we do not get any messages.
|
||||
*/
|
||||
if (paging_bts->available_slots == 0) {
|
||||
paging_bts->credit_timer.cb = paging_give_credit;
|
||||
paging_bts->credit_timer.data = paging_bts;
|
||||
bsc_schedule_timer(&paging_bts->credit_timer, 5, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(paging_bts->last_request);
|
||||
initial_request = paging_bts->last_request;
|
||||
current_request = initial_request;
|
||||
request = llist_entry(paging_bts->pending_requests.next,
|
||||
struct gsm_paging_request, entry);
|
||||
|
||||
do {
|
||||
/* handle the paging request now */
|
||||
page_ms(current_request);
|
||||
paging_bts->available_slots--;
|
||||
/* we need to determine the number of free channels */
|
||||
if (paging_bts->free_chans_need != -1) {
|
||||
if (can_send_pag_req(request->bts, request->chan_type) != 0)
|
||||
goto skip_paging;
|
||||
}
|
||||
|
||||
/*
|
||||
* move to the next item. We might wrap around
|
||||
* this means last_request will be NULL and we just
|
||||
* call paging_page_to_next again. It it guranteed
|
||||
* that the list is not empty.
|
||||
*/
|
||||
paging_move_to_next(paging_bts);
|
||||
if (!paging_bts->last_request)
|
||||
paging_bts->last_request =
|
||||
(struct gsm_paging_request *)paging_bts->pending_requests.next;
|
||||
current_request = paging_bts->last_request;
|
||||
} while (paging_bts->available_slots > 0
|
||||
&& initial_request != current_request);
|
||||
/* handle the paging request now */
|
||||
page_ms(request);
|
||||
paging_bts->available_slots--;
|
||||
|
||||
bsc_schedule_timer(&paging_bts->work_timer, 1, 0);
|
||||
/* take the current and add it to the back */
|
||||
llist_del(&request->entry);
|
||||
llist_add_tail(&request->entry, &paging_bts->pending_requests);
|
||||
|
||||
skip_paging:
|
||||
bsc_schedule_timer(&paging_bts->work_timer, PAGING_TIMER);
|
||||
}
|
||||
|
||||
static void paging_worker(void *data)
|
||||
@@ -178,7 +233,7 @@ void paging_init(struct gsm_bts *bts)
|
||||
bts->paging.work_timer.data = &bts->paging;
|
||||
|
||||
/* Large number, until we get a proper message */
|
||||
bts->paging.available_slots = 100;
|
||||
bts->paging.available_slots = 20;
|
||||
}
|
||||
|
||||
static int paging_pending_request(struct gsm_bts_paging_state *bts,
|
||||
@@ -200,7 +255,7 @@ static void paging_T3113_expired(void *data)
|
||||
void *cbfn_param;
|
||||
gsm_cbfn *cbfn;
|
||||
|
||||
DEBUGP(DPAG, "T3113 expired for request %p (%s)\n",
|
||||
LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n",
|
||||
req, req->subscr->imsi);
|
||||
|
||||
sig_data.subscr = req->subscr;
|
||||
@@ -208,11 +263,11 @@ static void paging_T3113_expired(void *data)
|
||||
sig_data.lchan = NULL;
|
||||
|
||||
/* must be destroyed before calling cbfn, to prevent double free */
|
||||
counter_inc(req->bts->network->stats.paging.expired);
|
||||
cbfn_param = req->cbfn_param;
|
||||
cbfn = req->cbfn;
|
||||
paging_remove_request(&req->bts->paging, req);
|
||||
|
||||
counter_inc(req->bts->network->stats.paging.expired);
|
||||
|
||||
dispatch_signal(SS_PAGING, S_PAGING_EXPIRED, &sig_data);
|
||||
if (cbfn)
|
||||
@@ -227,11 +282,11 @@ static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
|
||||
struct gsm_paging_request *req;
|
||||
|
||||
if (paging_pending_request(bts_entry, subscr)) {
|
||||
DEBUGP(DPAG, "Paging request already pending\n");
|
||||
LOGP(DPAG, LOGL_INFO, "Paging request already pending for %s\n", subscr->imsi);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
DEBUGP(DPAG, "Start paging of subscriber %llu on bts %d.\n",
|
||||
LOGP(DPAG, LOGL_DEBUG, "Start paging of subscriber %llu on bts %d.\n",
|
||||
subscr->id, bts->nr);
|
||||
req = talloc_zero(tall_paging_ctx, struct gsm_paging_request);
|
||||
req->subscr = subscr_get(subscr);
|
||||
@@ -243,9 +298,7 @@ static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
|
||||
req->T3113.data = req;
|
||||
bsc_schedule_timer(&req->T3113, bts->network->T3113, 0);
|
||||
llist_add_tail(&req->entry, &bts_entry->pending_requests);
|
||||
|
||||
if (!bsc_timer_pending(&bts_entry->work_timer))
|
||||
bsc_schedule_timer(&bts_entry->work_timer, 1, 0);
|
||||
paging_schedule_if_needed(bts_entry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -296,11 +349,11 @@ static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *sub
|
||||
entry) {
|
||||
if (req->subscr == subscr) {
|
||||
if (lchan && req->cbfn) {
|
||||
DEBUGP(DPAG, "Stop paging on bts %d, calling cbfn.\n", bts->nr);
|
||||
LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d, calling cbfn.\n", bts->nr);
|
||||
req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED,
|
||||
NULL, lchan, req->cbfn_param);
|
||||
} else
|
||||
DEBUGP(DPAG, "Stop paging on bts %d silently.\n", bts->nr);
|
||||
LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d silently.\n", bts->nr);
|
||||
paging_remove_request(&bts->paging, req);
|
||||
break;
|
||||
}
|
||||
@@ -335,5 +388,19 @@ void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr,
|
||||
|
||||
void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t free_slots)
|
||||
{
|
||||
bsc_del_timer(&bts->paging.credit_timer);
|
||||
bts->paging.available_slots = free_slots;
|
||||
paging_schedule_if_needed(&bts->paging);
|
||||
}
|
||||
|
||||
unsigned int paging_pending_requests_nr(struct gsm_bts *bts)
|
||||
{
|
||||
unsigned int requests = 0;
|
||||
|
||||
struct gsm_paging_request *req;
|
||||
|
||||
llist_for_each_entry(req, &bts->paging.pending_requests, entry)
|
||||
++requests;
|
||||
|
||||
return requests;
|
||||
}
|
||||
|
||||
@@ -133,6 +133,7 @@ static int append_lsa_params(struct bitvec *bv,
|
||||
const struct gsm48_lsa_params *lsa_params)
|
||||
{
|
||||
/* FIXME */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */
|
||||
@@ -318,8 +319,31 @@ static int append_gprs_cell_opt(struct bitvec *bv,
|
||||
/* hard-code no PAN_{DEC,INC,MAX} */
|
||||
bitvec_set_bit(bv, 0);
|
||||
|
||||
/* no extension information (EDGE) */
|
||||
bitvec_set_bit(bv, 0);
|
||||
if (!gco->ext_info_present) {
|
||||
/* no extension information */
|
||||
bitvec_set_bit(bv, 0);
|
||||
} else {
|
||||
/* extension information */
|
||||
bitvec_set_bit(bv, 1);
|
||||
if (!gco->ext_info.egprs_supported) {
|
||||
/* 6bit length of extension */
|
||||
bitvec_set_uint(bv, (1 + 3)-1, 6);
|
||||
/* EGPRS supported in the cell */
|
||||
bitvec_set_bit(bv, 0);
|
||||
} else {
|
||||
/* 6bit length of extension */
|
||||
bitvec_set_uint(bv, (1 + 5 + 3)-1, 6);
|
||||
/* EGPRS supported in the cell */
|
||||
bitvec_set_bit(bv, 1);
|
||||
/* 1bit EGPRS PACKET CHANNEL REQUEST */
|
||||
bitvec_set_bit(bv, gco->ext_info.use_egprs_p_ch_req);
|
||||
/* 4bit BEP PERIOD */
|
||||
bitvec_set_uint(bv, gco->ext_info.bep_period, 4);
|
||||
}
|
||||
bitvec_set_bit(bv, gco->ext_info.pfc_supported);
|
||||
bitvec_set_bit(bv, gco->ext_info.dtm_supported);
|
||||
bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -334,7 +358,7 @@ static void append_gprs_pwr_ctrl_pars(struct bitvec *bv,
|
||||
bitvec_set_uint(bv, pcp->n_avg_i, 4);
|
||||
}
|
||||
|
||||
/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */
|
||||
/* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */
|
||||
int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13)
|
||||
{
|
||||
struct bitvec bv;
|
||||
@@ -390,6 +414,11 @@ int rest_octets_si13(u_int8_t *data, const struct gsm48_si13_info *si13)
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* 3GPP TS 44.018 Release 6 / 10.5.2.37b */
|
||||
bitvec_set_bit(&bv, H); /* added Release 99 */
|
||||
/* claim our SGSN is compatible with Release 99, as EDGE and EGPRS
|
||||
* was only added in this Release */
|
||||
bitvec_set_bit(&bv, 1);
|
||||
}
|
||||
bitvec_spare_padding(&bv, (bv.data_len*8)-1);
|
||||
return bv.data_len;
|
||||
|
||||
@@ -156,7 +156,7 @@ static int handle_ser_read(struct bsc_fd *bfd)
|
||||
fprintf(stderr, "Invalid length in hdr: %u\n",
|
||||
sh->rxmsg_bytes_missing);
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
/* try to read as many of the missing bytes as are available */
|
||||
rc = read(sh->fd.fd, msg->tail, sh->rxmsg_bytes_missing);
|
||||
if (rc < 0) {
|
||||
|
||||
@@ -504,7 +504,7 @@ static int rtp_bfd_cb(struct bsc_fd *bfd, unsigned int flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_rss(struct rtp_sub_socket *rss,
|
||||
static void init_rss(struct rtp_sub_socket *rss,
|
||||
struct rtp_socket *rs, int fd, int priv_nr)
|
||||
{
|
||||
/* initialize bfd */
|
||||
|
||||
@@ -45,7 +45,7 @@ const struct sockaddr_sccp sccp_ssn_bssap = {
|
||||
|
||||
struct sccp_system {
|
||||
/* layer3 -> layer2 */
|
||||
int (*write_data)(struct msgb *data, void *context);
|
||||
void (*write_data)(struct msgb *data, void *context);
|
||||
void *write_context;
|
||||
};
|
||||
|
||||
@@ -81,7 +81,7 @@ static struct sccp_data_callback *_find_ssn(u_int8_t ssn)
|
||||
/* need to add one */
|
||||
cb = talloc_zero(tall_sccp_ctx, struct sccp_data_callback);
|
||||
if (!cb) {
|
||||
DEBUGP(DSCCP, "Failed to allocate sccp callback.\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Failed to allocate sccp callback.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -91,9 +91,9 @@ static struct sccp_data_callback *_find_ssn(u_int8_t ssn)
|
||||
}
|
||||
|
||||
|
||||
static int _send_msg(struct msgb *msg)
|
||||
static void _send_msg(struct msgb *msg)
|
||||
{
|
||||
return sccp_system.write_data(msg, sccp_system.write_context);
|
||||
sccp_system.write_data(msg, sccp_system.write_context);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -108,13 +108,13 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb
|
||||
u_int8_t length;
|
||||
|
||||
if (room <= 0) {
|
||||
DEBUGP(DSCCP, "Not enough room for an address: %u\n", room);
|
||||
LOGP(DSCCP, LOGL_ERROR, "Not enough room for an address: %u\n", room);
|
||||
return -1;
|
||||
}
|
||||
|
||||
length = msgb->l2h[offset];
|
||||
if (room <= length) {
|
||||
DEBUGP(DSCCP, "Not enough room for optional data %u %u\n", room, length);
|
||||
LOGP(DSCCP, LOGL_ERROR, "Not enough room for optional data %u %u\n", room, length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb
|
||||
party = (struct sccp_called_party_address *)(msgb->l2h + offset + 1);
|
||||
if (party->point_code_indicator) {
|
||||
if (length <= read + 2) {
|
||||
DEBUGP(DSCCP, "POI does not fit %u\n", length);
|
||||
LOGP(DSCCP, LOGL_ERROR, "POI does not fit %u\n", length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb
|
||||
|
||||
if (party->ssn_indicator) {
|
||||
if (length <= read + 1) {
|
||||
DEBUGP(DSCCP, "SSN does not fit %u\n", length);
|
||||
LOGP(DSCCP, LOGL_ERROR, "SSN does not fit %u\n", length);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb
|
||||
}
|
||||
|
||||
if (party->global_title_indicator) {
|
||||
DEBUGP(DSCCP, "GTI not supported %u\n", *(u_int8_t *)party);
|
||||
LOGP(DSCCP, LOGL_ERROR, "GTI not supported %u\n", *(u_int8_t *)party);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -156,7 +156,8 @@ static int check_address(struct sccp_address *addr)
|
||||
if (addr->address.ssn_indicator != 1
|
||||
|| addr->address.global_title_indicator == 1
|
||||
|| addr->address.routing_indicator != 1) {
|
||||
DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
LOGP(DSCCP, LOGL_ERROR,
|
||||
"Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
*(u_int8_t *)&addr->address, addr->ssn);
|
||||
return -1;
|
||||
}
|
||||
@@ -176,7 +177,7 @@ static int _sccp_parse_optional_data(const int offset,
|
||||
return 0;
|
||||
|
||||
if (read + 1 >= room) {
|
||||
DEBUGP(DSCCP, "no place for length\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "no place for length\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -185,7 +186,8 @@ static int _sccp_parse_optional_data(const int offset,
|
||||
|
||||
|
||||
if (room <= read) {
|
||||
DEBUGP(DSCCP, "no space for the data: type: %d read: %d room: %d l2: %d\n",
|
||||
LOGP(DSCCP, LOGL_ERROR,
|
||||
"no space for the data: type: %d read: %d room: %d l2: %d\n",
|
||||
type, read, room, msgb_l2len(msgb));
|
||||
return 0;
|
||||
}
|
||||
@@ -214,7 +216,7 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *
|
||||
|
||||
/* header check */
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb < header_size %u %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
@@ -224,7 +226,7 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *
|
||||
return -1;
|
||||
|
||||
if (check_address(&result->called) != 0) {
|
||||
DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
*(u_int8_t *)&result->called.address, result->called.ssn);
|
||||
return -1;
|
||||
}
|
||||
@@ -236,7 +238,7 @@ int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *
|
||||
*/
|
||||
memset(&optional_data, 0, sizeof(optional_data));
|
||||
if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) {
|
||||
DEBUGP(DSCCP, "parsing of optional data failed.\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -260,14 +262,14 @@ int _sccp_parse_connection_released(struct msgb *msgb, struct sccp_parse_result
|
||||
|
||||
/* we don't have enough size for the struct */
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb > header_size %u %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "msgb > header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&optional_data, 0, sizeof(optional_data));
|
||||
if (_sccp_parse_optional_data(optional_offset + rls->optional_start, msgb, &optional_data) != 0) {
|
||||
DEBUGP(DSCCP, "parsing of optional data failed.\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -295,7 +297,7 @@ int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result *
|
||||
|
||||
/* header check */
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb < header_size %u %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
@@ -306,7 +308,7 @@ int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result *
|
||||
|
||||
memset(&optional_data, 0, sizeof(optional_data));
|
||||
if (_sccp_parse_optional_data(optional_offset + ref->optional_start, msgb, &optional_data) != 0) {
|
||||
DEBUGP(DSCCP, "parsing of optional data failed.\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -333,7 +335,7 @@ int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result *
|
||||
|
||||
/* header check */
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb < header_size %u %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
@@ -344,7 +346,7 @@ int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result *
|
||||
|
||||
memset(&optional_data, 0, sizeof(optional_data));
|
||||
if (_sccp_parse_optional_data(optional_offset + con->optional_start, msgb, &optional_data) != 0) {
|
||||
DEBUGP(DSCCP, "parsing of optional data failed.\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "parsing of optional data failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -366,7 +368,7 @@ int _sccp_parse_connection_release_complete(struct msgb *msgb, struct sccp_parse
|
||||
|
||||
/* header check */
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb < header_size %u %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
@@ -387,13 +389,13 @@ int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *resu
|
||||
|
||||
/* we don't have enough size for the struct */
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb > header_size %u %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "msgb > header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dt1->segmenting != 0) {
|
||||
DEBUGP(DSCCP, "This packet has segmenting, not supported: %d\n", dt1->segmenting);
|
||||
LOGP(DSCCP, LOGL_ERROR, "This packet has segmenting, not supported: %d\n", dt1->segmenting);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -401,7 +403,7 @@ int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *resu
|
||||
|
||||
/* some more size checks in here */
|
||||
if (msgb_l2len(msgb) < variable_offset + dt1->variable_start + 1) {
|
||||
DEBUGP(DSCCP, "Not enough space for variable start: %u %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "Not enough space for variable start: %u %u\n",
|
||||
msgb_l2len(msgb), dt1->variable_start);
|
||||
return -1;
|
||||
}
|
||||
@@ -410,7 +412,7 @@ int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *resu
|
||||
msgb->l3h = &msgb->l2h[dt1->variable_start + variable_offset + 1];
|
||||
|
||||
if (msgb_l3len(msgb) < result->data_len) {
|
||||
DEBUGP(DSCCP, "Not enough room for the payload: %u %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "Not enough room for the payload: %u %u\n",
|
||||
msgb_l3len(msgb), result->data_len);
|
||||
return -1;
|
||||
}
|
||||
@@ -428,7 +430,7 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
|
||||
struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h;
|
||||
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb < header_size %u %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
@@ -438,7 +440,7 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
|
||||
return -1;
|
||||
|
||||
if (check_address(&result->called) != 0) {
|
||||
DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
*(u_int8_t *)&result->called.address, result->called.ssn);
|
||||
return -1;
|
||||
}
|
||||
@@ -447,13 +449,13 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
|
||||
return -1;
|
||||
|
||||
if (check_address(&result->calling) != 0) {
|
||||
DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "Invalid called address according to 08.06: 0x%x 0x%x\n",
|
||||
*(u_int8_t *)&result->called.address, result->called.ssn);
|
||||
}
|
||||
|
||||
/* we don't have enough size for the data */
|
||||
if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) {
|
||||
DEBUGP(DSCCP, "msgb < header + offset %u %u %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "msgb < header + offset %u %u %u\n",
|
||||
msgb_l2len(msgb), header_size, udt->variable_data);
|
||||
return -1;
|
||||
}
|
||||
@@ -463,7 +465,7 @@ int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
|
||||
result->data_len = msgb_l3len(msgb);
|
||||
|
||||
if (msgb_l3len(msgb) != msgb->l3h[-1]) {
|
||||
DEBUGP(DSCCP, "msgb is truncated is: %u should: %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "msgb is truncated is: %u should: %u\n",
|
||||
msgb_l3len(msgb), msgb->l3h[-1]);
|
||||
return -1;
|
||||
}
|
||||
@@ -478,7 +480,7 @@ static int _sccp_parse_it(struct msgb *msgb, struct sccp_parse_result *result)
|
||||
struct sccp_data_it *it;
|
||||
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
DEBUGP(DSCCP, "msgb < header_size %u %u\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
@@ -490,6 +492,23 @@ static int _sccp_parse_it(struct msgb *msgb, struct sccp_parse_result *result)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _sccp_parse_err(struct msgb *msgb, struct sccp_parse_result *result)
|
||||
{
|
||||
static const u_int32_t header_size = sizeof(struct sccp_proto_err);
|
||||
|
||||
struct sccp_proto_err *err;
|
||||
|
||||
if (msgb_l2len(msgb) < header_size) {
|
||||
LOGP(DSCCP, LOGL_ERROR, "msgb < header_size %u %u\n",
|
||||
msgb_l2len(msgb), header_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = (struct sccp_proto_err *) msgb->l2h;
|
||||
result->data_len = 0;
|
||||
result->destination_local_reference = &err->destination_local_reference;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send UDT. Currently we have a fixed address...
|
||||
@@ -499,10 +518,9 @@ static int _sccp_send_data(int class, const struct sockaddr_sccp *in,
|
||||
{
|
||||
struct sccp_data_unitdata *udt;
|
||||
u_int8_t *data;
|
||||
int ret;
|
||||
|
||||
if (msgb_l3len(payload) > 256) {
|
||||
DEBUGP(DSCCP, "The payload is too big for one udt\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "The payload is too big for one udt\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -533,10 +551,8 @@ static int _sccp_send_data(int class, const struct sockaddr_sccp *in,
|
||||
data[0] = msgb_l3len(payload);
|
||||
memcpy(&data[1], payload->l3h, msgb_l3len(payload));
|
||||
|
||||
ret = _send_msg(msg);
|
||||
msgb_free(msg);
|
||||
|
||||
return ret;
|
||||
_send_msg(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _sccp_handle_read(struct msgb *msgb)
|
||||
@@ -549,7 +565,7 @@ static int _sccp_handle_read(struct msgb *msgb)
|
||||
|
||||
cb = _find_ssn(result.called.ssn);
|
||||
if (!cb || !cb->read_cb) {
|
||||
DEBUGP(DSCCP, "No routing for UDT for called SSN: %u\n", result.called.ssn);
|
||||
LOGP(DSCCP, LOGL_ERROR, "No routing for UDT for called SSN: %u\n", result.called.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -598,7 +614,7 @@ static int assign_source_local_reference(struct sccp_connection *connection)
|
||||
++last_ref;
|
||||
/* do not use the reversed word and wrap around */
|
||||
if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) {
|
||||
DEBUGP(DSCCP, "Wrapped searching for a free code\n");
|
||||
LOGP(DSCCP, LOGL_DEBUG, "Wrapped searching for a free code\n");
|
||||
last_ref = 0;
|
||||
++wrapped;
|
||||
}
|
||||
@@ -609,7 +625,7 @@ static int assign_source_local_reference(struct sccp_connection *connection)
|
||||
}
|
||||
} while (wrapped != 2);
|
||||
|
||||
DEBUGP(DSCCP, "Finding a free reference failed\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Finding a free reference failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -627,7 +643,6 @@ static int _sccp_send_refuse(struct sccp_source_reference *src_ref, int cause)
|
||||
struct msgb *msgb;
|
||||
struct sccp_connection_refused *ref;
|
||||
u_int8_t *data;
|
||||
int ret;
|
||||
|
||||
msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
|
||||
SCCP_MSG_HEADROOM, "sccp ref");
|
||||
@@ -643,9 +658,8 @@ static int _sccp_send_refuse(struct sccp_source_reference *src_ref, int cause)
|
||||
data = msgb_put(msgb, 1);
|
||||
data[0] = SCCP_PNC_END_OF_OPTIONAL;
|
||||
|
||||
ret = _send_msg(msgb);
|
||||
msgb_free(msgb);
|
||||
return ret;
|
||||
_send_msg(msgb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _sccp_send_connection_confirm(struct sccp_connection *connection)
|
||||
@@ -653,7 +667,6 @@ static int _sccp_send_connection_confirm(struct sccp_connection *connection)
|
||||
struct msgb *response;
|
||||
struct sccp_connection_confirm *confirm;
|
||||
u_int8_t *optional_data;
|
||||
int ret;
|
||||
|
||||
if (assign_source_local_reference(connection) != 0)
|
||||
return -1;
|
||||
@@ -677,11 +690,9 @@ static int _sccp_send_connection_confirm(struct sccp_connection *connection)
|
||||
optional_data = (u_int8_t *) msgb_put(response, 1);
|
||||
optional_data[0] = SCCP_PNC_END_OF_OPTIONAL;
|
||||
|
||||
ret = _send_msg(response);
|
||||
msgb_free(response);
|
||||
|
||||
_send_msg(response);
|
||||
_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_ESTABLISHED);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _sccp_send_connection_request(struct sccp_connection *connection,
|
||||
@@ -691,17 +702,16 @@ static int _sccp_send_connection_request(struct sccp_connection *connection,
|
||||
struct sccp_connection_request *req;
|
||||
u_int8_t *data;
|
||||
u_int8_t extra_size = 3 + 1;
|
||||
int ret;
|
||||
|
||||
|
||||
if (msg && (msgb_l3len(msg) < 3 || msgb_l3len(msg) > 130)) {
|
||||
DEBUGP(DSCCP, "Invalid amount of data... %d\n", msgb_l3len(msg));
|
||||
LOGP(DSCCP, LOGL_ERROR, "Invalid amount of data... %d\n", msgb_l3len(msg));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* try to find a id */
|
||||
if (assign_source_local_reference(connection) != 0) {
|
||||
DEBUGP(DSCCP, "Assigning a local reference failed.\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Assigning a local reference failed.\n");
|
||||
_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_SETUP_ERROR);
|
||||
return -1;
|
||||
}
|
||||
@@ -741,10 +751,8 @@ static int _sccp_send_connection_request(struct sccp_connection *connection,
|
||||
llist_add_tail(&connection->list, &sccp_connections);
|
||||
_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REQUEST);
|
||||
|
||||
ret = _send_msg(request);
|
||||
msgb_free(request);
|
||||
|
||||
return ret;
|
||||
_send_msg(request);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb *_data)
|
||||
@@ -753,10 +761,9 @@ static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb
|
||||
struct sccp_data_form1 *dt1;
|
||||
u_int8_t *data;
|
||||
int extra_size;
|
||||
int ret;
|
||||
|
||||
if (msgb_l3len(_data) < 2 || msgb_l3len(_data) > 256) {
|
||||
DEBUGP(DSCCP, "data size too big, segmenting unimplemented.\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "data size too big, segmenting unimplemented.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -777,17 +784,14 @@ static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb
|
||||
data[0] = extra_size - 1;
|
||||
memcpy(&data[1], _data->l3h, extra_size - 1);
|
||||
|
||||
ret = _send_msg(msgb);
|
||||
msgb_free(msgb);
|
||||
|
||||
return ret;
|
||||
_send_msg(msgb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _sccp_send_connection_it(struct sccp_connection *conn)
|
||||
{
|
||||
struct msgb *msgb;
|
||||
struct sccp_data_it *it;
|
||||
int ret;
|
||||
|
||||
msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
|
||||
SCCP_MSG_HEADROOM, "sccp it");
|
||||
@@ -803,9 +807,8 @@ static int _sccp_send_connection_it(struct sccp_connection *conn)
|
||||
it->sequencing[0] = it->sequencing[1] = 0;
|
||||
it->credit = 0;
|
||||
|
||||
ret = _send_msg(msgb);
|
||||
msgb_free(msgb);
|
||||
return ret;
|
||||
_send_msg(msgb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _sccp_send_connection_released(struct sccp_connection *conn, int cause)
|
||||
@@ -813,7 +816,6 @@ static int _sccp_send_connection_released(struct sccp_connection *conn, int caus
|
||||
struct msgb *msg;
|
||||
struct sccp_connection_released *rel;
|
||||
u_int8_t *data;
|
||||
int ret;
|
||||
|
||||
msg = msgb_alloc_headroom(SCCP_MSG_SIZE, SCCP_MSG_HEADROOM,
|
||||
"sccp: connection released");
|
||||
@@ -832,10 +834,8 @@ static int _sccp_send_connection_released(struct sccp_connection *conn, int caus
|
||||
data[0] = SCCP_PNC_END_OF_OPTIONAL;
|
||||
|
||||
_sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE);
|
||||
ret = _send_msg(msg);
|
||||
msgb_free(msg);
|
||||
|
||||
return ret;
|
||||
_send_msg(msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -859,14 +859,14 @@ static int _sccp_handle_connection_request(struct msgb *msgb)
|
||||
|
||||
cb = _find_ssn(result.called.ssn);
|
||||
if (!cb || !cb->accept_cb) {
|
||||
DEBUGP(DSCCP, "No routing for CR for called SSN: %u\n", result.called.ssn);
|
||||
LOGP(DSCCP, LOGL_ERROR, "No routing for CR for called SSN: %u\n", result.called.ssn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* check if the system wants this connection */
|
||||
connection = talloc_zero(tall_sccp_ctx, struct sccp_connection);
|
||||
if (!connection) {
|
||||
DEBUGP(DSCCP, "Allocation failed\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -878,7 +878,7 @@ static int _sccp_handle_connection_request(struct msgb *msgb)
|
||||
* one....
|
||||
*/
|
||||
if (destination_local_reference_is_free(result.source_local_reference) != 0) {
|
||||
DEBUGP(DSCCP, "Need to reject connection with existing reference\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Need to reject connection with existing reference\n");
|
||||
_sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE);
|
||||
talloc_free(connection);
|
||||
return -1;
|
||||
@@ -898,7 +898,7 @@ static int _sccp_handle_connection_request(struct msgb *msgb)
|
||||
llist_add_tail(&connection->list, &sccp_connections);
|
||||
|
||||
if (_sccp_send_connection_confirm(connection) != 0) {
|
||||
DEBUGP(DSCCP, "Sending confirm failed... no available source reference?\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Sending confirm failed... no available source reference?\n");
|
||||
|
||||
_sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE);
|
||||
_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
|
||||
@@ -941,7 +941,7 @@ static int _sccp_handle_connection_release_complete(struct msgb *msgb)
|
||||
}
|
||||
|
||||
|
||||
DEBUGP(DSCCP, "Release complete of unknown connection\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Release complete of unknown connection\n");
|
||||
return -1;
|
||||
|
||||
found:
|
||||
@@ -969,7 +969,7 @@ static int _sccp_handle_connection_dt1(struct msgb *msgb)
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGP(DSCCP, "No connection found for dt1 data\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "No connection found for dt1 data\n");
|
||||
return -1;
|
||||
|
||||
found:
|
||||
@@ -982,7 +982,6 @@ static int _sccp_send_connection_release_complete(struct sccp_connection *connec
|
||||
{
|
||||
struct msgb *msgb;
|
||||
struct sccp_connection_release_complete *rlc;
|
||||
int ret;
|
||||
|
||||
msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
|
||||
SCCP_MSG_HEADROOM, "sccp rlc");
|
||||
@@ -995,8 +994,7 @@ static int _sccp_send_connection_release_complete(struct sccp_connection *connec
|
||||
memcpy(&rlc->source_local_reference,
|
||||
&connection->source_local_reference, sizeof(struct sccp_source_reference));
|
||||
|
||||
ret = _send_msg(msgb);
|
||||
msgb_free(msgb);
|
||||
_send_msg(msgb);
|
||||
|
||||
/*
|
||||
* Remove from the list of active connections and set the state. User code
|
||||
@@ -1004,8 +1002,7 @@ static int _sccp_send_connection_release_complete(struct sccp_connection *connec
|
||||
*/
|
||||
llist_del(&connection->list);
|
||||
_sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_RELEASE_COMPLETE);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* connection released, send a released confirm */
|
||||
@@ -1031,7 +1028,7 @@ static int _sccp_handle_connection_released(struct msgb *msgb)
|
||||
}
|
||||
|
||||
|
||||
DEBUGP(DSCCP, "Unknown connection was released.\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Unknown connection was released.\n");
|
||||
return -1;
|
||||
|
||||
/* we have found a connection */
|
||||
@@ -1043,7 +1040,7 @@ found:
|
||||
|
||||
/* generate a response */
|
||||
if (_sccp_send_connection_release_complete(conn) != 0) {
|
||||
DEBUGP(DSCCP, "Sending release confirmed failed\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Sending release confirmed failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1068,7 +1065,7 @@ static int _sccp_handle_connection_refused(struct msgb *msgb)
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGP(DSCCP, "Refused but no connection found\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Refused but no connection found\n");
|
||||
return -1;
|
||||
|
||||
found:
|
||||
@@ -1101,7 +1098,7 @@ static int _sccp_handle_connection_confirm(struct msgb *msgb)
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGP(DSCCP, "Confirmed but no connection found\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Confirmed but no connection found\n");
|
||||
return -1;
|
||||
|
||||
found:
|
||||
@@ -1118,7 +1115,7 @@ found:
|
||||
}
|
||||
|
||||
|
||||
int sccp_system_init(int (*outgoing)(struct msgb *data, void *ctx), void *ctx)
|
||||
int sccp_system_init(void (*outgoing)(struct msgb *data, void *ctx), void *ctx)
|
||||
{
|
||||
sccp_system.write_data = outgoing;
|
||||
sccp_system.write_context = ctx;
|
||||
@@ -1130,7 +1127,7 @@ int sccp_system_init(int (*outgoing)(struct msgb *data, void *ctx), void *ctx)
|
||||
int sccp_system_incoming(struct msgb *msgb)
|
||||
{
|
||||
if (msgb_l2len(msgb) < 1 ) {
|
||||
DEBUGP(DSCCP, "Too short packet\n");
|
||||
LOGP(DSCCP, LOGL_ERROR, "Too short packet\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1159,7 +1156,7 @@ int sccp_system_incoming(struct msgb *msgb)
|
||||
return _sccp_handle_read(msgb);
|
||||
break;
|
||||
default:
|
||||
DEBUGP(DSCCP, "unimplemented msg type: %d\n", type);
|
||||
LOGP(DSCCP, LOGL_ERROR, "unimplemented msg type: %d\n", type);
|
||||
};
|
||||
|
||||
return -1;
|
||||
@@ -1170,7 +1167,7 @@ int sccp_connection_write(struct sccp_connection *connection, struct msgb *data)
|
||||
{
|
||||
if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
|
||||
|| connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
|
||||
DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "sccp_connection_write: Wrong connection state: %p %d\n",
|
||||
connection, connection->connection_state);
|
||||
return -1;
|
||||
}
|
||||
@@ -1187,7 +1184,7 @@ int sccp_connection_send_it(struct sccp_connection *connection)
|
||||
{
|
||||
if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
|
||||
|| connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
|
||||
DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "sccp_connection_write: Wrong connection state: %p %d\n",
|
||||
connection, connection->connection_state);
|
||||
return -1;
|
||||
}
|
||||
@@ -1200,7 +1197,7 @@ int sccp_connection_close(struct sccp_connection *connection, int cause)
|
||||
{
|
||||
if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
|
||||
|| connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
|
||||
DEBUGPC(DSCCP, "Can not close the connection. It was never opened: %p %d\n",
|
||||
LOGP(DSCCP, LOGL_ERROR, "Can not close the connection. It was never opened: %p %d\n",
|
||||
connection, connection->connection_state);
|
||||
return -1;
|
||||
}
|
||||
@@ -1212,7 +1209,7 @@ int sccp_connection_free(struct sccp_connection *connection)
|
||||
{
|
||||
if (connection->connection_state > SCCP_CONNECTION_STATE_NONE
|
||||
&& connection->connection_state < SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
|
||||
DEBUGP(DSCCP, "The connection needs to be released before it is freed");
|
||||
LOGP(DSCCP, LOGL_ERROR, "The connection needs to be released before it is freed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1220,6 +1217,17 @@ int sccp_connection_free(struct sccp_connection *connection)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sccp_connection_force_free(struct sccp_connection *con)
|
||||
{
|
||||
if (con->connection_state > SCCP_CONNECTION_STATE_NONE &&
|
||||
con->connection_state < SCCP_CONNECTION_STATE_RELEASE_COMPLETE)
|
||||
llist_del(&con->list);
|
||||
|
||||
con->connection_state = SCCP_CONNECTION_STATE_REFUSED;
|
||||
sccp_connection_free(con);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sccp_connection *sccp_connection_socket(void)
|
||||
{
|
||||
return talloc_zero(tall_sccp_ctx, struct sccp_connection);
|
||||
@@ -1329,6 +1337,9 @@ int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result)
|
||||
case SCCP_MSG_TYPE_IT:
|
||||
return _sccp_parse_it(msg, result);
|
||||
break;
|
||||
case SCCP_MSG_TYPE_ERR:
|
||||
return _sccp_parse_err(msg, result);
|
||||
break;
|
||||
};
|
||||
|
||||
LOGP(DSCCP, LOGL_ERROR, "Unimplemented MSG Type: 0x%x\n", type);
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
static int paging_cb_silent(unsigned int hooknum, unsigned int event,
|
||||
struct msgb *msg, void *_lchan, void *_data)
|
||||
{
|
||||
struct gsm_subscriber_connection *conn;
|
||||
struct gsm_lchan *lchan = _lchan;
|
||||
struct scall_signal_data sigdata;
|
||||
int rc;
|
||||
@@ -47,6 +48,8 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event,
|
||||
|
||||
DEBUGP(DSMS, "paging_cb_silent: ");
|
||||
|
||||
conn = &lchan->conn;
|
||||
|
||||
sigdata.lchan = lchan;
|
||||
sigdata.data = _data;
|
||||
|
||||
@@ -54,10 +57,10 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event,
|
||||
case GSM_PAGING_SUCCEEDED:
|
||||
DEBUGPC(DSMS, "success, using Timeslot %u on ARFCN %u\n",
|
||||
lchan->ts->nr, lchan->ts->trx->arfcn);
|
||||
lchan->silent_call = 1;
|
||||
conn->silent_call = 1;
|
||||
/* increment lchan reference count */
|
||||
dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata);
|
||||
use_lchan(lchan);
|
||||
use_subscr_con(conn);
|
||||
break;
|
||||
case GSM_PAGING_EXPIRED:
|
||||
DEBUGP(DSMS, "expired\n");
|
||||
@@ -97,7 +100,7 @@ int silent_call_reroute(struct msgb *msg)
|
||||
int i;
|
||||
|
||||
/* if we're not part of a silent call, never reroute */
|
||||
if (!msg->lchan->silent_call)
|
||||
if (!msg->lchan->conn.silent_call)
|
||||
return 0;
|
||||
|
||||
/* check if we are a special message that is handled in openbsc */
|
||||
@@ -126,16 +129,18 @@ int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type)
|
||||
int gsm_silent_call_stop(struct gsm_subscriber *subscr)
|
||||
{
|
||||
struct gsm_lchan *lchan;
|
||||
struct gsm_subscriber_connection *conn;
|
||||
|
||||
lchan = lchan_for_subscr(subscr);
|
||||
if (!lchan)
|
||||
return -EINVAL;
|
||||
|
||||
/* did we actually establish a silent call for this guy? */
|
||||
if (!lchan->silent_call)
|
||||
conn = &lchan->conn;
|
||||
if (!conn->silent_call)
|
||||
return -EINVAL;
|
||||
|
||||
put_lchan(lchan);
|
||||
put_subscr_con(conn, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user