mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-10-31 12:03:50 +00:00 
			
		
		
		
	Compare commits
	
		
			638 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ed7c4fcfc3 | ||
|  | 3babd1a803 | ||
|  | bc3f3b40fe | ||
|  | fae09f4562 | ||
|  | 7c0fe31697 | ||
|  | 98aef217c6 | ||
|  | e03e34f8bb | ||
|  | ee6958c9a8 | ||
|  | fbcf4a6f6c | ||
|  | 58dc88bc31 | ||
|  | aac84edc9d | ||
|  | 172f5acfce | ||
|  | fdbefde869 | ||
|  | 25ecc91c3b | ||
|  | 995529548b | ||
|  | 0c04500ac5 | ||
|  | a0b69f1896 | ||
|  | fbf78d13f1 | ||
|  | df9192efee | ||
|  | 70a658a2ba | ||
|  | 2764bdb1aa | ||
|  | 55fdfc223e | ||
|  | 8f33303660 | ||
|  | a94c56e4c6 | ||
|  | 2ebcf5c34a | ||
|  | f936e10f07 | ||
|  | e303fa9ff3 | ||
|  | 958f5e74cc | ||
|  | 1e8d5fa44b | ||
|  | 374ad788d8 | ||
|  | da07b90f0f | ||
|  | fa495d669f | ||
|  | 439c86fc58 | ||
|  | 74e83d3d82 | ||
|  | d55be05af1 | ||
|  | 8f91a7f450 | ||
|  | da3a5759e1 | ||
|  | 29e671f257 | ||
|  | d6a7e17911 | ||
|  | 3f4a4cb49c | ||
|  | 3d2b76fd95 | ||
|  | 276a414aa3 | ||
|  | c534ad17dc | ||
|  | 3f2c15f275 | ||
|  | 38533ba9b3 | ||
|  | 39889e4389 | ||
|  | c824fe4eb0 | ||
|  | 124a3e0b34 | ||
|  | 96c6e06681 | ||
|  | ce18705875 | ||
|  | 41ab87f67c | ||
|  | a8739fc7be | ||
|  | 21dfeff8aa | ||
|  | f486e741a4 | ||
|  | 268568593e | ||
|  | 41d59205c0 | ||
|  | 036612b035 | ||
|  | 6bad138c96 | ||
|  | 8dc3597085 | ||
|  | d70eef6421 | ||
|  | 33d97f721d | ||
|  | d57310731f | ||
|  | a8c684b51f | ||
|  | 03fcc91aad | ||
|  | 9de30e7cc4 | ||
|  | d64c041cdb | ||
|  | d02716d6c2 | ||
|  | a065e632c0 | ||
|  | bd060c3c99 | ||
|  | 97cae477fb | ||
|  | 02c880a7dd | ||
|  | ba09687768 | ||
|  | a4b677c45a | ||
|  | 6b7afe8f57 | ||
|  | 4131a65c4d | ||
|  | 6a5e5ac2d4 | ||
|  | b066bd0c86 | ||
|  | d6769ea207 | ||
|  | ca280a1a84 | ||
|  | a24dcc61d7 | ||
|  | 62d9df684f | ||
|  | 8358c4ba07 | ||
|  | 4c77e9b748 | ||
|  | 33347a4f5d | ||
|  | 7d86d4c523 | ||
|  | eb984bd630 | ||
|  | b3d14eb552 | ||
|  | 907744e2fc | ||
|  | 776846a0b9 | ||
|  | 59e7cf4437 | ||
|  | b2064423fe | ||
|  | 97a9312be8 | ||
|  | 427cede4ba | ||
|  | 7371663aeb | ||
|  | fe53edd776 | ||
|  | 4f6a7ad5f8 | ||
|  | 55a9229922 | ||
|  | 7802ccc8d6 | ||
|  | 3ff5151661 | ||
|  | 5936a9c23e | ||
|  | 9ffaba7c1b | ||
|  | 535ede2598 | ||
|  | 6ccdaa243f | ||
|  | ace0b946ff | ||
|  | a34036c296 | ||
|  | 9fec391d6e | ||
|  | a835fa85d7 | ||
|  | 2c66c15227 | ||
|  | 92a73cd637 | ||
|  | 6e2795bfbf | ||
|  | 276b595361 | ||
|  | 3c293b418b | ||
|  | 41425e9b85 | ||
|  | 31682a3274 | ||
|  | 870f94b493 | ||
|  | 242e9ada91 | ||
|  | 9b444fd417 | ||
|  | e9473f610e | ||
|  | 53002b3a00 | ||
|  | a098b65252 | ||
|  | 78b0055e5e | ||
|  | 648bec3982 | ||
|  | c2a8f59560 | ||
|  | 38404969df | ||
|  | 19c430feba | ||
|  | 3ba409558e | ||
|  | 60be627557 | ||
|  | 8cfe7df3f1 | ||
|  | ba94b6d7be | ||
|  | 30e01355f6 | ||
|  | 06624e13d4 | ||
|  | 19539866e5 | ||
|  | 1dc2dcebbf | ||
|  | 6049a63d1d | ||
|  | 3ef8e76534 | ||
|  | fbf07f3f69 | ||
|  | de133113a0 | ||
|  | 6d0a59a1bf | ||
|  | 2491401932 | ||
|  | fbbe8f2f98 | ||
|  | 71d42e778a | ||
|  | 8a2a1b22fe | ||
|  | a790f0c082 | ||
|  | 0ab152b2c6 | ||
|  | 2741d6bb0e | ||
|  | 74d0e5c318 | ||
|  | c4ef4a21c6 | ||
|  | ee2f33bf9a | ||
|  | 531470a0ca | ||
|  | 9dc73593a3 | ||
|  | ae6858bddf | ||
|  | 99d4d368e8 | ||
|  | 8667d5169d | ||
|  | 1add5a53cb | ||
|  | 729bf3e45a | ||
|  | 2f34b53b5b | ||
|  | ad79f9eb99 | ||
|  | 9e494e67c7 | ||
|  | 5322473d04 | ||
|  | c57ad7ff9a | ||
|  | c59b5c533d | ||
|  | 0653cc8a7c | ||
|  | 246233d0d4 | ||
|  | a910a81b7c | ||
|  | c8acee2234 | ||
|  | 55863e42c1 | ||
|  | 6177cae2a2 | ||
|  | 6af3ccf65d | ||
|  | 03cb5f3397 | ||
|  | 113141d4f9 | ||
|  | 993ea6be7a | ||
|  | 889fe7f203 | ||
|  | efd09d0b77 | ||
|  | ffbc618a13 | ||
|  | 563ffc51b5 | ||
|  | 51b42ff2e1 | ||
|  | 7a755be4c2 | ||
|  | 06ea49159e | ||
|  | 869b21c869 | ||
|  | 37a808c5a5 | ||
|  | 24b8c0ce92 | ||
|  | fe67e094ad | ||
|  | fbcf39976e | ||
|  | 4863591c23 | ||
|  | 827da2c867 | ||
|  | efe1571b7b | ||
|  | 6521fc0695 | ||
|  | f5531845bc | ||
|  | 13fae78b32 | ||
|  | b7d395d83a | ||
|  | 6fbbeec064 | ||
|  | 0ffa3bdc45 | ||
|  | ca8639dc05 | ||
|  | 8d6a193c1a | ||
|  | 58a1ba85c7 | ||
|  | b0c05aa3a9 | ||
|  | 0996a1e4ae | ||
|  | 7e9ddc9904 | ||
|  | 04bbb9de3e | ||
|  | bea56788cb | ||
|  | 48bcc2ee12 | ||
|  | 080935a8c7 | ||
|  | a466f57007 | ||
|  | 41f77d8018 | ||
|  | e674345e29 | ||
|  | 3f8139c55f | ||
|  | 98c09b3f30 | ||
|  | 7462b95829 | ||
|  | 7a64182f9a | ||
|  | d19de2ee80 | ||
|  | 08eb9352ab | ||
|  | 0b79d21c7f | ||
|  | bce5f29265 | ||
|  | 3d5a2dd19f | ||
|  | 7f90ddb519 | ||
|  | c66ab2c4c3 | ||
|  | f53796c1fe | ||
|  | 14b27a8893 | ||
|  | 21be42abed | ||
|  | 2d681fd84c | ||
|  | c39b1bffec | ||
|  | 62612e8575 | ||
|  | af932ce3bc | ||
|  | 265b0a8045 | ||
|  | 74390c521e | ||
|  | abfb858f0c | ||
|  | 6500d72aaf | ||
|  | d6877eb8f9 | ||
|  | ebb9bf3f12 | ||
|  | 61cf9bb5f1 | ||
|  | a7152e055a | ||
|  | 63866009e2 | ||
|  | dab89af070 | ||
|  | eebbf2b1fc | ||
|  | e885bc5c24 | ||
|  | 173dc129fc | ||
|  | 3abced8d64 | ||
|  | a48ff4a738 | ||
|  | 6c92f9d83e | ||
|  | 9852e22101 | ||
|  | b141cccbfb | ||
|  | ec45068972 | ||
|  | 832bcdf631 | ||
|  | 73f9c02f49 | ||
|  | 6a25a61142 | ||
|  | 1dbbed169a | ||
|  | a8f27abe12 | ||
|  | ca2aec0235 | ||
|  | e827831514 | ||
|  | 923d60bb12 | ||
|  | f2bf8dc8c8 | ||
|  | 055ded74de | ||
|  | 3ff71284fa | ||
|  | cc0b97e197 | ||
|  | 8c69e29820 | ||
|  | 843d9038ce | ||
|  | d071a30238 | ||
|  | 3ab8ca4d84 | ||
|  | 23f4048b57 | ||
|  | 401b740ccd | ||
|  | a468b0f57f | ||
|  | b0cfa7272e | ||
|  | 683e05f60b | ||
|  | 16b637bf1b | ||
|  | 2698540c1e | ||
|  | d2f5e69d3e | ||
|  | ce64f18587 | ||
|  | 667fa59b0c | ||
|  | 5a6220f43b | ||
|  | 7c6dd3c2c3 | ||
|  | 740af6ed44 | ||
|  | 782d607962 | ||
|  | 50e52e45f9 | ||
|  | a2d10216ea | ||
|  | f54eb96338 | ||
|  | a2b1c5e6f6 | ||
|  | c5c1430a1c | ||
|  | f7de9aea7b | ||
|  | 199c5e965e | ||
|  | 06a49fc04b | ||
|  | 6349bd422a | ||
|  | 796a4a1325 | ||
|  | 2cf6652ba2 | ||
|  | 9b508f6b53 | ||
|  | 6e26c70f46 | ||
|  | 9a34592c09 | ||
|  | 7eb6f2cb56 | ||
|  | 83fd8a5692 | ||
|  | 17058484d1 | ||
|  | 166077ea48 | ||
|  | 0c6c3c1da6 | ||
|  | fe9a1fe03b | ||
|  | 1e4a34d45e | ||
|  | 189f29e939 | ||
|  | d2ce444008 | ||
|  | ef9420adf5 | ||
|  | cb96e05a91 | ||
|  | f6387dc766 | ||
|  | 1b1d7ed98f | ||
|  | df7d97e4b4 | ||
|  | 85978dadab | ||
|  | 9aaaab6b3b | ||
|  | c1ad553d86 | ||
|  | c1bf4694e7 | ||
|  | 1442c5a99e | ||
|  | dac2ca7e21 | ||
|  | 375252279c | ||
|  | 295570c631 | ||
|  | a93c6e9263 | ||
|  | 30907dc9d8 | ||
|  | 14f8a08f44 | ||
|  | c63f15a9a7 | ||
|  | ca538fc5eb | ||
|  | 6be2c49538 | ||
|  | 91088c305f | ||
|  | 900cd6518a | ||
|  | fa810e8ccd | ||
|  | 497611ae84 | ||
|  | 2b89617aad | ||
|  | b542b0457b | ||
|  | 120568651a | ||
|  | f1d301a9a6 | ||
|  | c9a6280c94 | ||
|  | b5583cde41 | ||
|  | 6c1cd63a57 | ||
|  | 0d7ba56f5c | ||
|  | 3ac604e3ad | ||
|  | 9fb8ddf00e | ||
|  | 182ca3bad4 | ||
|  | f027f17dcb | ||
|  | ac772d8b0c | ||
|  | 9d9f44ac71 | ||
|  | 84274e4e9b | ||
|  | c5479fe086 | ||
|  | 538d2c53d9 | ||
|  | 8de58e79b8 | ||
|  | 5e8d7995d1 | ||
|  | 3239f6212e | ||
|  | 209eb9f103 | ||
|  | ef6304e4a1 | ||
|  | 21cdbfc196 | ||
|  | fc8067348b | ||
|  | 9f11dc5616 | ||
|  | ca0818c760 | ||
|  | c12bfb7ffc | ||
|  | 228e591589 | ||
|  | 58128258b0 | ||
|  | e7ae69a7be | ||
|  | 7e37ce6008 | ||
|  | dbe09dd515 | ||
|  | 217d31d62f | ||
|  | 47642f245d | ||
|  | cb760bdebe | ||
|  | e3f8bca424 | ||
|  | 9fc8a02196 | ||
|  | e36b775eae | ||
|  | 622dd61764 | ||
|  | e81c1176aa | ||
|  | ed0c1aa9d5 | ||
|  | ac69ea9cdf | ||
|  | 0a40379214 | ||
|  | 72bc8da537 | ||
|  | c3132fd508 | ||
|  | 96c3107daf | ||
|  | ad21a0e166 | ||
|  | 32d15cc8ba | ||
|  | 1fd50e5306 | ||
|  | 6802374574 | ||
|  | 82c1d1eb20 | ||
|  | 5a5169e15c | ||
|  | 7c0b70a94e | ||
|  | ca6a8495e1 | ||
|  | c0a2196f91 | ||
|  | da026abc27 | ||
|  | 27780313ac | ||
|  | e5ed946bdb | ||
|  | c319b79baa | ||
|  | 2818db8b33 | ||
|  | 0415de74b8 | ||
|  | 4075b8805d | ||
|  | 8ab3fbbaf2 | ||
|  | cc39218dce | ||
|  | 8dbc9ed408 | ||
|  | ba25eab0fa | ||
|  | aa823bf24b | ||
|  | a714abfc91 | ||
|  | 9270e91c09 | ||
|  | b7974e2fa4 | ||
|  | 1e174875bf | ||
|  | c26b665c0c | ||
|  | 0479b2a15f | ||
|  | ff6606cacb | ||
|  | 407b1f186d | ||
|  | de2a4d7c22 | ||
|  | 48aff62341 | ||
|  | 852ba86949 | ||
|  | 662fa421c6 | ||
|  | d14163e74f | ||
|  | 11b4810142 | ||
|  | 9ecceb651b | ||
|  | 426a9d9103 | ||
|  | 17bf603222 | ||
|  | bcd52e5724 | ||
|  | 956242dcec | ||
|  | e686675f7d | ||
|  | 4ca0b0d1fa | ||
|  | efd645e5a8 | ||
|  | f0504e86b3 | ||
|  | e28b673fba | ||
|  | a77eade744 | ||
|  | 6531726a02 | ||
|  | 31b4729f27 | ||
|  | a729db62a9 | ||
|  | 5336f57f05 | ||
|  | eb72ff058f | ||
|  | 8a91d2c04e | ||
|  | 55e0dcf254 | ||
|  | 23e7bf1c00 | ||
|  | 10d487e13f | ||
|  | 40f5033cf9 | ||
|  | b1bb1fa187 | ||
|  | 1d121483f6 | ||
|  | 124441af1f | ||
|  | b861db97ea | ||
|  | 08e07046f9 | ||
|  | a5e0cf09a6 | ||
|  | f2388eab71 | ||
|  | 120865833c | ||
|  | e6d8e91b3a | ||
|  | 475f868b7f | ||
|  | 0063ca2fb0 | ||
|  | 610fda6108 | ||
|  | 352eed09ae | ||
|  | 0a89e92a5e | ||
|  | dde80f3f61 | ||
|  | 9a7ccc3746 | ||
|  | 910189d0a1 | ||
|  | 8482e8374c | ||
|  | fee4fa9492 | ||
|  | 4dba769577 | ||
|  | da895b1151 | ||
|  | a74c0ea2db | ||
|  | 6a26c16eb0 | ||
|  | af8e00ffe8 | ||
|  | 6931f9a7a4 | ||
|  | 544448abea | ||
|  | e547bdd1ce | ||
|  | d1562a7766 | ||
|  | 304b3eb328 | ||
|  | 9cd5233609 | ||
|  | 35a382968c | ||
|  | 7066af825a | ||
|  | acc10353fe | ||
|  | bca0ef6cd9 | ||
|  | cede2a4b7c | ||
|  | 337209a6ea | ||
|  | 56e0443e1c | ||
|  | b1a1b4000e | ||
|  | 8838c62fe9 | ||
|  | 704c4f0adf | ||
|  | bc0346e080 | ||
|  | 5928dc9345 | ||
|  | 04da5e5e98 | ||
|  | 54b4f82f91 | ||
|  | 3d7b58d77a | ||
|  | 604410cd13 | ||
|  | b340f90c9e | ||
|  | 3c8ccb6724 | ||
|  | d4e6aa42ca | ||
|  | e6df0e47e7 | ||
|  | 54eb0e1204 | ||
|  | b38fb8911f | ||
|  | 1b3a385b9d | ||
|  | dbd70c7b68 | ||
|  | a19547b7a1 | ||
|  | 06823731d8 | ||
|  | ed1cff5ab9 | ||
|  | f2321b7a72 | ||
|  | 4219904cb2 | ||
|  | b2753f2044 | ||
|  | ba61f68137 | ||
|  | 9e1d164469 | ||
|  | 0ec1d4e17c | ||
|  | d761d355f9 | ||
|  | 8d064dfd24 | ||
|  | 7181cc1f02 | ||
|  | 60f8e31a2f | ||
|  | 086c3f3c67 | ||
|  | d0b470d1a9 | ||
|  | 77f76d0be5 | ||
|  | 892dec0be9 | ||
|  | c430d19112 | ||
|  | 7b8e419d92 | ||
|  | 4f7613eeb1 | ||
|  | 01f039538e | ||
|  | d2e3a52230 | ||
|  | 2924825bc6 | ||
|  | bd86f94a09 | ||
|  | 1083533db4 | ||
|  | 0793d2f5d5 | ||
|  | a8c6a9c37a | ||
|  | 2da99a2946 | ||
|  | c3eed40f00 | ||
|  | 515e341ff6 | ||
|  | 8bda7a7766 | ||
|  | df5d219f39 | ||
|  | 5656fbf49d | ||
|  | 19d640e806 | ||
|  | 207ab51270 | ||
|  | c3cc654add | ||
|  | b911b879d8 | ||
|  | af07f66ca3 | ||
|  | dd0c522cd4 | ||
|  | 37d11c80da | ||
|  | 1355d7e4f7 | ||
|  | fdd603c4c8 | ||
|  | 1fc6999a77 | ||
|  | 220f487238 | ||
|  | edc00f4ea7 | ||
|  | a49e32a1ab | ||
|  | dd4ede34ad | ||
|  | 3aa815755d | ||
|  | 3261dc7540 | ||
|  | 771b26a043 | ||
|  | 36a81129ec | ||
|  | a390d0ba52 | ||
|  | b759473d10 | ||
|  | c3cfae2cf8 | ||
|  | 3cbfb8a53c | ||
|  | 275ac97036 | ||
|  | abe8c897fd | ||
|  | e9d645b3b3 | ||
|  | 7f0966c13d | ||
|  | 55295f7b07 | ||
|  | 9d25d7a2e6 | ||
|  | 490cbaa89e | ||
|  | eb0bde09bb | ||
|  | 3b12e1b011 | ||
|  | 2138779559 | ||
|  | 12943ea0c1 | ||
|  | 6efb43a210 | ||
|  | 03cc48474c | ||
|  | 5672563f7e | ||
|  | 106743c66b | ||
|  | 8890dfa634 | ||
|  | 10f32dbfbf | ||
|  | e35eeae8e0 | ||
|  | abbb6b9088 | ||
|  | 3f35a3708a | ||
|  | 33eafe050b | ||
|  | 1d1b98f4a4 | ||
|  | a0ac30faa5 | ||
|  | 49e3d5a9c9 | ||
|  | 3338135ead | ||
|  | 5dbfc78aff | ||
|  | 7bc5552512 | ||
|  | a330b864e5 | ||
|  | 7df419b434 | ||
|  | 7cedfd753b | ||
|  | 23b8e29835 | ||
|  | 1c3287f1db | ||
|  | 922ae9a5bf | ||
|  | 189d6bf4d4 | ||
|  | c8f37cb4d6 | ||
|  | c0dcc3c60c | ||
|  | 230e4fc270 | ||
|  | ead2f60f73 | ||
|  | f8bfbe8b14 | ||
|  | ffd75e420c | ||
|  | 01d24a3281 | ||
|  | 0be3ce66c0 | ||
|  | fcd0655176 | ||
|  | 48454983c4 | ||
|  | 9a3543a8d4 | ||
|  | 465446b4b4 | ||
|  | b597b4fc9d | ||
|  | d20910c655 | ||
|  | 677f4ad968 | ||
|  | 7c20c9de5a | ||
|  | 839fc954d0 | ||
|  | d164b05655 | ||
|  | a896f9cdeb | ||
|  | 1b0cf6fa1c | ||
|  | 9bf7c53779 | ||
|  | 6be0fdb5e4 | ||
|  | 641c4d47b9 | ||
|  | c1b9fa158e | ||
|  | 6a421f1b6f | ||
|  | f1889d8640 | ||
|  | 2982e42d93 | ||
|  | ddf1f9d7d5 | ||
|  | 653c4ff354 | ||
|  | 4efce88a62 | ||
|  | e7d27aeae1 | ||
|  | e6f172dd57 | ||
|  | b969455941 | ||
|  | 54dd4b3f72 | ||
|  | dbd88e5167 | ||
|  | 87203f2a37 | ||
|  | 1cb1e38dbc | ||
|  | e726d4fad2 | ||
|  | c341388b30 | ||
|  | 7bf4ce3aed | ||
|  | f4c0e37352 | ||
|  | 06da85ed3a | ||
|  | 1dc6be6a82 | ||
|  | 8348abb372 | ||
|  | d8d7b4b188 | ||
|  | 31c4305225 | ||
|  | c7a228aceb | ||
|  | e472b4e39a | ||
|  | 8970c497bc | ||
|  | 1e0b9f872c | ||
|  | 8863f7ae64 | ||
|  | 1ae2d5ebf4 | ||
|  | 5852a78d2e | ||
|  | 59e2d518fd | ||
|  | 062b0f0eee | ||
|  | 71c619ee05 | ||
|  | e3b5de2285 | ||
|  | f96e46fcce | ||
|  | 88d4dae645 | ||
|  | 8042539444 | ||
|  | 6ba4406c47 | ||
|  | 1e940d615a | ||
|  | fde78ad412 | ||
|  | 3d9b656a1c | ||
|  | 2152f8523e | ||
|  | c5ecebb711 | ||
|  | 020e89bce9 | ||
|  | 6779354245 | ||
|  | 87bd9be0b0 | ||
|  | f83ec56212 | ||
|  | 1c8d67d7f5 | ||
|  | 9d4f1d57f3 | ||
|  | d95ab1e9ad | ||
|  | a1fa2f4bc3 | ||
|  | c221b7ad01 | 
							
								
								
									
										118
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								.clang-format
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | |||||||
|  | # SPDX-License-Identifier: GPL-2.0 | ||||||
|  | # | ||||||
|  | # clang-format configuration file. Intended for clang-format >= 4. | ||||||
|  | # | ||||||
|  | # For more information, see: | ||||||
|  | # | ||||||
|  | #   Documentation/process/clang-format.rst | ||||||
|  | #   https://clang.llvm.org/docs/ClangFormat.html | ||||||
|  | #   https://clang.llvm.org/docs/ClangFormatStyleOptions.html | ||||||
|  | # | ||||||
|  | --- | ||||||
|  | AccessModifierOffset: -4 | ||||||
|  | AlignAfterOpenBracket: Align | ||||||
|  | AlignConsecutiveAssignments: false | ||||||
|  | AlignConsecutiveDeclarations: false | ||||||
|  | #AlignEscapedNewlines: Left # Unknown to clang-format-4.0 | ||||||
|  | AlignOperands: true | ||||||
|  | AlignTrailingComments: false | ||||||
|  | AllowAllParametersOfDeclarationOnNextLine: false | ||||||
|  | AllowShortBlocksOnASingleLine: false | ||||||
|  | AllowShortCaseLabelsOnASingleLine: false | ||||||
|  | AllowShortFunctionsOnASingleLine: None | ||||||
|  | AllowShortIfStatementsOnASingleLine: false | ||||||
|  | AllowShortLoopsOnASingleLine: false | ||||||
|  | AlwaysBreakAfterDefinitionReturnType: None | ||||||
|  | AlwaysBreakAfterReturnType: None | ||||||
|  | AlwaysBreakBeforeMultilineStrings: false | ||||||
|  | AlwaysBreakTemplateDeclarations: false | ||||||
|  | BinPackArguments: true | ||||||
|  | BinPackParameters: true | ||||||
|  | BraceWrapping: | ||||||
|  |   AfterClass: false | ||||||
|  |   AfterControlStatement: false | ||||||
|  |   AfterEnum: false | ||||||
|  |   AfterFunction: true | ||||||
|  |   AfterNamespace: true | ||||||
|  |   AfterObjCDeclaration: false | ||||||
|  |   AfterStruct: false | ||||||
|  |   AfterUnion: false | ||||||
|  |   #AfterExternBlock: false # Unknown to clang-format-5.0 | ||||||
|  |   BeforeCatch: false | ||||||
|  |   BeforeElse: false | ||||||
|  |   IndentBraces: false | ||||||
|  |   #SplitEmptyFunction: true # Unknown to clang-format-4.0 | ||||||
|  |   #SplitEmptyRecord: true # Unknown to clang-format-4.0 | ||||||
|  |   #SplitEmptyNamespace: true # Unknown to clang-format-4.0 | ||||||
|  | BreakBeforeBinaryOperators: None | ||||||
|  | BreakBeforeBraces: Custom | ||||||
|  | #BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0 | ||||||
|  | BreakBeforeTernaryOperators: false | ||||||
|  | BreakConstructorInitializersBeforeComma: false | ||||||
|  | #BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 | ||||||
|  | BreakAfterJavaFieldAnnotations: false | ||||||
|  | BreakStringLiterals: false | ||||||
|  | ColumnLimit: 120 | ||||||
|  | CommentPragmas: '^ IWYU pragma:' | ||||||
|  | #CompactNamespaces: false # Unknown to clang-format-4.0 | ||||||
|  | ConstructorInitializerAllOnOneLineOrOnePerLine: false | ||||||
|  | ConstructorInitializerIndentWidth: 8 | ||||||
|  | ContinuationIndentWidth: 8 | ||||||
|  | Cpp11BracedListStyle: false | ||||||
|  | DerivePointerAlignment: false | ||||||
|  | DisableFormat: false | ||||||
|  | ExperimentalAutoDetectBinPacking: false | ||||||
|  | #FixNamespaceComments: false # Unknown to clang-format-4.0 | ||||||
|  |  | ||||||
|  | #IncludeBlocks: Preserve # Unknown to clang-format-5.0 | ||||||
|  | IncludeCategories: | ||||||
|  |   - Regex: '.*' | ||||||
|  |     Priority: 1 | ||||||
|  | IncludeIsMainRegex: '(Test)?$' | ||||||
|  | IndentCaseLabels: false | ||||||
|  | #IndentPPDirectives: None # Unknown to clang-format-5.0 | ||||||
|  | IndentWidth: 8 | ||||||
|  | IndentWrappedFunctionNames: false | ||||||
|  | JavaScriptQuotes: Leave | ||||||
|  | JavaScriptWrapImports: true | ||||||
|  | KeepEmptyLinesAtTheStartOfBlocks: false | ||||||
|  | MacroBlockBegin: '' | ||||||
|  | MacroBlockEnd: '' | ||||||
|  | MaxEmptyLinesToKeep: 1 | ||||||
|  | NamespaceIndentation: None | ||||||
|  | #ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 | ||||||
|  | ObjCBlockIndentWidth: 8 | ||||||
|  | ObjCSpaceAfterProperty: true | ||||||
|  | ObjCSpaceBeforeProtocolList: true | ||||||
|  |  | ||||||
|  | # Taken from git's rules | ||||||
|  | #PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0 | ||||||
|  | PenaltyBreakBeforeFirstCallParameter: 30 | ||||||
|  | PenaltyBreakComment: 10 | ||||||
|  | PenaltyBreakFirstLessLess: 0 | ||||||
|  | PenaltyBreakString: 10 | ||||||
|  | PenaltyExcessCharacter: 100 | ||||||
|  | PenaltyReturnTypeOnItsOwnLine: 60 | ||||||
|  |  | ||||||
|  | PointerAlignment: Right | ||||||
|  | ReflowComments: false | ||||||
|  | SortIncludes: false | ||||||
|  | #SortUsingDeclarations: false # Unknown to clang-format-4.0 | ||||||
|  | SpaceAfterCStyleCast: false | ||||||
|  | SpaceAfterTemplateKeyword: true | ||||||
|  | SpaceBeforeAssignmentOperators: true | ||||||
|  | #SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0 | ||||||
|  | #SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0 | ||||||
|  | SpaceBeforeParens: ControlStatements | ||||||
|  | #SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0 | ||||||
|  | SpaceInEmptyParentheses: false | ||||||
|  | SpacesBeforeTrailingComments: 1 | ||||||
|  | SpacesInAngles: false | ||||||
|  | SpacesInContainerLiterals: false | ||||||
|  | SpacesInCStyleCastParentheses: false | ||||||
|  | SpacesInParentheses: false | ||||||
|  | SpacesInSquareBrackets: false | ||||||
|  | Standard: Cpp03 | ||||||
|  | TabWidth: 8 | ||||||
|  | UseTab: Always | ||||||
|  | ... | ||||||
							
								
								
									
										58
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										58
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -2,26 +2,21 @@ debian/*.log | |||||||
| *.o | *.o | ||||||
| *.lo | *.lo | ||||||
| *.a | *.a | ||||||
|  | *.la | ||||||
| .deps | .deps | ||||||
| Makefile | Makefile | ||||||
| Makefile.in | Makefile.in | ||||||
| bscconfig.h | bscconfig.h | ||||||
| bscconfig.h.in | bscconfig.h.in | ||||||
| openbsc.pc | src/osmo-mgw/osmo-mgw | ||||||
| src/osmo-nitb/osmo-nitb |  | ||||||
| src/osmo-bsc_mgcp/osmo-bsc_mgcp |  | ||||||
| src/osmo-bsc/osmo-bsc |  | ||||||
| src/utils/meas_vis |  | ||||||
| src/utils/meas_json |  | ||||||
| src/utils/osmo-meas-pcap2db |  | ||||||
| src/utils/osmo-meas-udp2db |  | ||||||
| src/utils/smpp_mirror |  | ||||||
| *.*~ | *.*~ | ||||||
| *.sw? | *.sw? | ||||||
| .libs | .libs | ||||||
| *.pyc | *.pyc | ||||||
| *.gcda | *.gcda | ||||||
| *.gcno | *.gcno | ||||||
|  | *.pc | ||||||
|  | *~ | ||||||
|  |  | ||||||
| #configure | #configure | ||||||
| aclocal.m4 | aclocal.m4 | ||||||
| @@ -38,38 +33,16 @@ missing | |||||||
| stamp-h1 | stamp-h1 | ||||||
| libtool | libtool | ||||||
| ltmain.sh | ltmain.sh | ||||||
|  | m4/*.m4 | ||||||
|  |  | ||||||
| # git-version-gen magic | # git-version-gen magic | ||||||
| .tarball-version | .tarball-version | ||||||
| .version | .version | ||||||
|  |  | ||||||
|  |  | ||||||
| # apps and app data |  | ||||||
| hlr.sqlite3 |  | ||||||
| src/utils/bs11_config |  | ||||||
| src/ipaccess/ipaccess-config |  | ||||||
| src/ipaccess/abisip-find |  | ||||||
| src/ipaccess/ipaccess-firmware |  | ||||||
| src/ipaccess/ipaccess-proxy |  | ||||||
| src/utils/isdnsync |  | ||||||
| src/nat/bsc_nat |  | ||||||
| src/gprs/osmo-sgsn |  | ||||||
| src/gprs/osmo-gbproxy |  | ||||||
| src/gprs/osmo-gtphub |  | ||||||
| src/osmo-bsc_nat/osmo-bsc_nat |  | ||||||
| src/libcommon/gsup_test_client |  | ||||||
| src/osmo-msc/osmo-msc |  | ||||||
|  |  | ||||||
| #tests | #tests | ||||||
| tests/testsuite.dir | tests/testsuite.dir | ||||||
| tests/*/*_test | tests/*/*_test | ||||||
| # ignore compiled binaries like msc_vlr_test_foo; do not ignore |  | ||||||
| # msc_vlr_test_foo.{c,ok,err}, but do still ignore the corresponding .o object |  | ||||||
| # files: |  | ||||||
| tests/msc_vlr/msc_vlr_test_* |  | ||||||
| !tests/msc_vlr/msc_vlr_test_*.* |  | ||||||
| tests/msc_vlr/msc_vlr_test_*.o |  | ||||||
|  |  | ||||||
|  |  | ||||||
| tests/atconfig | tests/atconfig | ||||||
| tests/atlocal | tests/atlocal | ||||||
| @@ -77,7 +50,22 @@ tests/package.m4 | |||||||
| tests/testsuite | tests/testsuite | ||||||
| tests/testsuite.log | tests/testsuite.log | ||||||
|  |  | ||||||
| gsn_restart |  | ||||||
| src/openbsc.cfg* |  | ||||||
| writtenconfig/ | writtenconfig/ | ||||||
| gtphub_restart_count |  | ||||||
|  | # manuals | ||||||
|  | doc/manuals/*.html | ||||||
|  | doc/manuals/*.svg | ||||||
|  | doc/manuals/*.pdf | ||||||
|  | doc/manuals/*__*.png | ||||||
|  | doc/manuals/*.check | ||||||
|  | doc/manuals/generated/ | ||||||
|  | doc/manuals/osmomsc-usermanual.xml | ||||||
|  | doc/manuals/common | ||||||
|  | doc/manuals/build | ||||||
|  |  | ||||||
|  | contrib/osmo-mgw.spec | ||||||
|  |  | ||||||
|  | #vs code | ||||||
|  | .cache | ||||||
|  | .vscode | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								Makefile.am
									
									
									
									
									
								
							| @@ -9,21 +9,29 @@ AM_CPPFLAGS = \ | |||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
| SUBDIRS = \ | SUBDIRS = \ | ||||||
| 	doc \ |  | ||||||
| 	include \ | 	include \ | ||||||
| 	src \ | 	src \ | ||||||
| 	tests \ | 	tests \ | ||||||
|  | 	doc \ | ||||||
| 	contrib \ | 	contrib \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
| pkgconfigdir = $(libdir)/pkgconfig | pkgconfigdir = $(libdir)/pkgconfig | ||||||
| pkgconfig_DATA = \ | pkgconfig_DATA = \ | ||||||
| 	libosmo-legacy-mgcp.pc \ |  | ||||||
| 	libosmo-mgcp-client.pc \ | 	libosmo-mgcp-client.pc \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
| BUILT_SOURCES = $(top_srcdir)/.version | BUILT_SOURCES = $(top_srcdir)/.version | ||||||
| EXTRA_DIST = git-version-gen osmoappdesc.py .version | EXTRA_DIST = \ | ||||||
|  | 	     .version \ | ||||||
|  | 	     contrib/osmo-mgw.spec.in \ | ||||||
|  | 	     debian \ | ||||||
|  | 	     git-version-gen \ | ||||||
|  | 	     osmoappdesc.py \ | ||||||
|  | 	     $(NULL) | ||||||
|  |  | ||||||
|  | AM_DISTCHECK_CONFIGURE_FLAGS = \ | ||||||
|  | 	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) | ||||||
|  |  | ||||||
| @RELMAKE@ | @RELMAKE@ | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								README
									
									
									
									
									
								
							| @@ -19,10 +19,6 @@ OsmoBSC, and receives and sends RTP streams as configured via MGCP. | |||||||
| The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found | The libosmo-mgcp-client library exposes utilities used by e.g. OsmoMSC (found | ||||||
| in osmo-msc.git) to instruct OsmoMGW via its MGCP service. | in osmo-msc.git) to instruct OsmoMGW via its MGCP service. | ||||||
|  |  | ||||||
| The libosmo-mgcp library exposes MGCP server utilities used by e.g. OsmoBSC-NAT |  | ||||||
| (found in osmo-bsc.git) to navigate RTP streams through a NAT. |  | ||||||
| (At time of writing, this is still called libosmo-legacy-mgcp.) |  | ||||||
|  |  | ||||||
| Find OsmoMGW issue tracker and wiki online at | Find OsmoMGW issue tracker and wiki online at | ||||||
| https://osmocom.org/projects/osmo-mgw | https://osmocom.org/projects/osmo-mgw | ||||||
| https://osmocom.org/projects/osmo-mgw/wiki | https://osmocom.org/projects/osmo-mgw/wiki | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								TODO-RELEASE
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								TODO-RELEASE
									
									
									
									
									
								
							| @@ -1,9 +1,26 @@ | |||||||
| # When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install | # When cleaning up this file upon a release: | ||||||
| # according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info | # | ||||||
| # In short: | # - Note that the release version number is entirely unrelated to the API | ||||||
| # LIBVERSION=c:r:a | #   versions. A release version 5.2.3 may happily have an API version of 42:7:5. | ||||||
| # If the library source code has changed at all since the last update, then increment revision: c:r + 1:a. | # | ||||||
| # If any interfaces have been added, removed, or changed since the last update: c + 1:0:0. | # - Bump API version in src/lib*/Makefile.am files according to chapter | ||||||
| # If any interfaces have been added since the last public release: c:r:a + 1. | #   "Library interface versions" of the libtool documentation. | ||||||
| # If any interfaces have been removed or changed since the last public release: c:r:0. | #   https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html | ||||||
|  | # | ||||||
|  | # - Iff the 'current' API version has changed, rename debian/lib*.install | ||||||
|  | # | ||||||
|  | # API version bumping for the impatient: | ||||||
|  | # LIBVERSION=c:r:a  (current:revision_of_current:backwards_compat_age) | ||||||
|  | # 5:2:4 means that | ||||||
|  | # - this implements version 5 of the API; | ||||||
|  | # - this is the 2nd (compatible) revision of API version 5; | ||||||
|  | # - this is backwards compatible to all APIs since 4 versions ago, | ||||||
|  | #   i.e. callers that need API versions from 1 to 5 can use this. | ||||||
|  | # | ||||||
|  | # Bumping API versions recipe: | ||||||
|  | # If the library source code has changed at all since the last update, r++; | ||||||
|  | # If any interfaces have been added, removed, or changed since the last update, c++, r=0; | ||||||
|  | # If any interfaces have been added since the last public release, a++; | ||||||
|  | # If any interfaces have been removed or changed since the last public release, a=0. | ||||||
|  | # | ||||||
| #library		what		description / commit summary line | #library		what		description / commit summary line | ||||||
|   | |||||||
							
								
								
									
										159
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -9,6 +9,8 @@ AC_CONFIG_AUX_DIR([.]) | |||||||
| AM_INIT_AUTOMAKE([dist-bzip2]) | AM_INIT_AUTOMAKE([dist-bzip2]) | ||||||
| AC_CONFIG_TESTDIR(tests) | AC_CONFIG_TESTDIR(tests) | ||||||
|  |  | ||||||
|  | CFLAGS="$CFLAGS -std=gnu11" | ||||||
|  |  | ||||||
| dnl kernel style compile messages | dnl kernel style compile messages | ||||||
| m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) | ||||||
|  |  | ||||||
| @@ -22,6 +24,11 @@ AC_PROG_CC | |||||||
| AC_PROG_INSTALL | AC_PROG_INSTALL | ||||||
| LT_INIT | LT_INIT | ||||||
|  |  | ||||||
|  | dnl patching ${archive_cmds} to affect generation of file "libtool" to fix linking with clang | ||||||
|  | AS_CASE(["$LD"],[*clang*], | ||||||
|  |   [AS_CASE(["${host_os}"], | ||||||
|  |      [*linux*],[archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'])]) | ||||||
|  |  | ||||||
| dnl check for pkg-config (explained in detail in libosmocore/configure.ac) | dnl check for pkg-config (explained in detail in libosmocore/configure.ac) | ||||||
| AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no) | AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no) | ||||||
| if test "x$PKG_CONFIG_INSTALLED" = "xno"; then | if test "x$PKG_CONFIG_INSTALLED" = "xno"; then | ||||||
| @@ -29,55 +36,54 @@ if test "x$PKG_CONFIG_INSTALLED" = "xno"; then | |||||||
| fi | fi | ||||||
| PKG_PROG_PKG_CONFIG([0.20]) | PKG_PROG_PKG_CONFIG([0.20]) | ||||||
|  |  | ||||||
| dnl check for AX_CHECK_COMPILE_FLAG |  | ||||||
| m4_ifdef([AX_CHECK_COMPILE_FLAG], [], [ |  | ||||||
| 	AC_MSG_ERROR([Please install autoconf-archive; re-run 'autoreconf -fi' for it to take effect.]) |  | ||||||
| 	]) |  | ||||||
|  |  | ||||||
| dnl checks for libraries | dnl checks for libraries | ||||||
| AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""]) | AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""]) | ||||||
| AC_SUBST(LIBRARY_DL) | AC_SUBST(LIBRARY_DL) | ||||||
|  |  | ||||||
|  | AC_SEARCH_LIBS([dlsym], [dl dld], [LIBRARY_DLSYM="$LIBS";LIBS=""]) | ||||||
|  | AC_SUBST(LIBRARY_DLSYM) | ||||||
|  |  | ||||||
| PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.9.5) |  | ||||||
| PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.3.0) |  | ||||||
| PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.0.1) |  | ||||||
|  |  | ||||||
| # Enable/disable transcoding within osmo-bsc_mgcp? | PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0) | ||||||
| AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])], | PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.6.0) | ||||||
|     [osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"]) | PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0) | ||||||
| AC_ARG_WITH([g729], [AS_HELP_STRING([--with-g729], [Enable G.729 encoding/decoding.])], [osmo_ac_with_g729="$withval"],[osmo_ac_with_g729="no"]) | PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0) | ||||||
|  | PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 1.1.0) | ||||||
|  | PKG_CHECK_MODULES(LIBOSMOABIS, libosmoabis >= 1.2.0) | ||||||
|  | PKG_CHECK_MODULES(LIBOSMOTRAU, libosmotrau >= 1.2.0) | ||||||
|  |  | ||||||
| if test "$osmo_ac_mgcp_transcoding" = "yes" ; then | CFLAGS="$CFLAGS -pthread" | ||||||
|     AC_SEARCH_LIBS([gsm_create], [gsm], [LIBRARY_GSM="$LIBS";LIBS=""], [AC_MSG_ERROR([--enable-mgcp-transcoding: cannot find usable libgsm])]) | LDFLAGS="$LDFLAGS -pthread" | ||||||
|     AC_SUBST(LIBRARY_GSM) |  | ||||||
|     if test "$osmo_ac_with_g729" = "yes" ; then | AC_ARG_ENABLE(sanitize, | ||||||
| 	PKG_CHECK_MODULES(LIBBCG729, libbcg729 >= 0.1, [AC_DEFINE([HAVE_BCG729], [1], [Use bgc729 decoder/encoder])]) | 	[AS_HELP_STRING( | ||||||
|     fi | 		[--enable-sanitize], | ||||||
|     AC_DEFINE(BUILD_MGCP_TRANSCODING, 1, [Define if we want to build the MGCP gateway with transcoding support]) | 		[Compile with address sanitizer enabled], | ||||||
|  | 	)], | ||||||
|  | 	[sanitize=$enableval], [sanitize="no"]) | ||||||
|  | if test x"$sanitize" = x"yes" | ||||||
|  | then | ||||||
|  | 	CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined" | ||||||
|  | 	CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" | ||||||
| fi | fi | ||||||
| AM_CONDITIONAL(BUILD_MGCP_TRANSCODING, test "x$osmo_ac_mgcp_transcoding" = "xyes") |  | ||||||
| AC_SUBST(osmo_ac_mgcp_transcoding) |  | ||||||
|  |  | ||||||
| dnl Checks for typedefs, structures and compiler characteristics | AC_ARG_ENABLE(werror, | ||||||
|  | 	[AS_HELP_STRING( | ||||||
| # The following test is taken from WebKit's webkit.m4 | 		[--enable-werror], | ||||||
| saved_CFLAGS="$CFLAGS" | 		[Turn all compiler warnings into errors, with exceptions: | ||||||
| CFLAGS="$CFLAGS -fvisibility=hidden " | 		 a) deprecation (allow upstream to mark deprecation without breaking builds); | ||||||
| AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) | 		 b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) | ||||||
| AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])], | 		] | ||||||
|       [ AC_MSG_RESULT([yes]) | 	)], | ||||||
|         SYMBOL_VISIBILITY="-fvisibility=hidden"], | 	[werror=$enableval], [werror="no"]) | ||||||
|         AC_MSG_RESULT([no])) | if test x"$werror" = x"yes" | ||||||
| CFLAGS="$saved_CFLAGS" | then | ||||||
| AC_SUBST(SYMBOL_VISIBILITY) | 	WERROR_FLAGS="-Werror" | ||||||
|  | 	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" | ||||||
| AX_CHECK_COMPILE_FLAG([-Werror=implicit], [CFLAGS="$CFLAGS -Werror=implicit"]) | 	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" | ||||||
| AX_CHECK_COMPILE_FLAG([-Werror=maybe-uninitialized], [CFLAGS="$CFLAGS -Werror=maybe-uninitialized"]) | 	CFLAGS="$CFLAGS $WERROR_FLAGS" | ||||||
| AX_CHECK_COMPILE_FLAG([-Werror=memset-transposed-args], [CFLAGS="$CFLAGS -Werror=memset-transposed-args"]) | 	CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" | ||||||
| AX_CHECK_COMPILE_FLAG([-Werror=null-dereference], [CFLAGS="$CFLAGS -Werror=null-dereference"]) | fi | ||||||
| AX_CHECK_COMPILE_FLAG([-Werror=sizeof-array-argument], [CFLAGS="$CFLAGS -Werror=sizeof-array-argument"]) |  | ||||||
| AX_CHECK_COMPILE_FLAG([-Werror=sizeof-pointer-memaccess], [CFLAGS="$CFLAGS -Werror=sizeof-pointer-memaccess"]) |  | ||||||
|  |  | ||||||
| # Coverage build taken from WebKit's configure.in | # Coverage build taken from WebKit's configure.in | ||||||
| AC_MSG_CHECKING([whether to enable code coverage support]) | AC_MSG_CHECKING([whether to enable code coverage support]) | ||||||
| @@ -113,25 +119,88 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests]) | |||||||
| AC_MSG_RESULT([$enable_ext_tests]) | AC_MSG_RESULT([$enable_ext_tests]) | ||||||
| AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes") | AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes") | ||||||
|  |  | ||||||
|  | # Generate manuals | ||||||
|  | AC_ARG_ENABLE(manuals, | ||||||
|  | 	[AS_HELP_STRING( | ||||||
|  | 		[--enable-manuals], | ||||||
|  | 		[Generate manual PDFs [default=no]], | ||||||
|  | 	)], | ||||||
|  | 	[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"]) | ||||||
|  | AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"]) | ||||||
|  | AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals" | ||||||
|  | 	fallback]) | ||||||
|  | if test x"$osmo_ac_build_manuals" = x"yes" | ||||||
|  | then | ||||||
|  | 	# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback) | ||||||
|  | 	if test -n "$OSMO_GSM_MANUALS_DIR"; then | ||||||
|  | 		echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)" | ||||||
|  | 	else | ||||||
|  | 		OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)" | ||||||
|  | 		if test -n "$OSMO_GSM_MANUALS_DIR"; then | ||||||
|  | 			echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)" | ||||||
|  | 		else | ||||||
|  | 			OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals" | ||||||
|  | 			echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)" | ||||||
|  | 		fi | ||||||
|  | 	fi | ||||||
|  | 	if ! test -d "$OSMO_GSM_MANUALS_DIR"; then | ||||||
|  | 		AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.") | ||||||
|  | 	fi | ||||||
|  |  | ||||||
|  | 	# Find and run check-depends | ||||||
|  | 	CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh" | ||||||
|  | 	if ! test -x "$CHECK_DEPENDS"; then | ||||||
|  | 		CHECK_DEPENDS="osmo-gsm-manuals-check-depends" | ||||||
|  | 	fi | ||||||
|  | 	if ! $CHECK_DEPENDS; then | ||||||
|  | 		AC_MSG_ERROR("missing dependencies for --enable-manuals") | ||||||
|  | 	fi | ||||||
|  |  | ||||||
|  | 	# Put in Makefile with absolute path | ||||||
|  | 	OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")" | ||||||
|  | 	AC_SUBST([OSMO_GSM_MANUALS_DIR]) | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # https://www.freedesktop.org/software/systemd/man/daemon.html | ||||||
|  | AC_ARG_WITH([systemdsystemunitdir], | ||||||
|  |      [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],, | ||||||
|  |      [with_systemdsystemunitdir=auto]) | ||||||
|  | AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [ | ||||||
|  |      def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) | ||||||
|  |  | ||||||
|  |      AS_IF([test "x$def_systemdsystemunitdir" = "x"], | ||||||
|  |    [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"], | ||||||
|  |     [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) | ||||||
|  |     with_systemdsystemunitdir=no], | ||||||
|  |    [with_systemdsystemunitdir="$def_systemdsystemunitdir"])]) | ||||||
|  | AS_IF([test "x$with_systemdsystemunitdir" != "xno"], | ||||||
|  |       [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])]) | ||||||
|  | AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"]) | ||||||
|  |  | ||||||
|  | AC_MSG_RESULT([CFLAGS="$CFLAGS"]) | ||||||
|  | AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) | ||||||
|  |  | ||||||
| dnl Generate the output | dnl Generate the output | ||||||
| AM_CONFIG_HEADER(bscconfig.h) | AM_CONFIG_HEADER(bscconfig.h) | ||||||
|  |  | ||||||
| AC_OUTPUT( | AC_OUTPUT( | ||||||
|     libosmo-legacy-mgcp.pc |  | ||||||
|     libosmo-mgcp-client.pc |     libosmo-mgcp-client.pc | ||||||
|     include/Makefile |     include/Makefile | ||||||
|     include/osmocom/Makefile |     include/osmocom/Makefile | ||||||
|     include/osmocom/legacy_mgcp/Makefile |  | ||||||
|     include/osmocom/mgcp_client/Makefile |     include/osmocom/mgcp_client/Makefile | ||||||
|  |     include/osmocom/mgcp/Makefile | ||||||
|     src/Makefile |     src/Makefile | ||||||
|     src/libosmo-legacy-mgcp/Makefile |  | ||||||
|     src/libosmo-mgcp-client/Makefile |     src/libosmo-mgcp-client/Makefile | ||||||
|     src/osmo-bsc_mgcp/Makefile |     src/libosmo-mgcp/Makefile | ||||||
|  |     src/osmo-mgw/Makefile | ||||||
|     tests/Makefile |     tests/Makefile | ||||||
|     tests/atlocal |     tests/atlocal | ||||||
|     tests/legacy_mgcp/Makefile |  | ||||||
|     tests/mgcp_client/Makefile |     tests/mgcp_client/Makefile | ||||||
|  |     tests/mgcp/Makefile | ||||||
|     doc/Makefile |     doc/Makefile | ||||||
|     doc/examples/Makefile |     doc/examples/Makefile | ||||||
|  |     doc/manuals/Makefile | ||||||
|     contrib/Makefile |     contrib/Makefile | ||||||
|  |     contrib/systemd/Makefile | ||||||
|  |     contrib/osmo-mgw.spec | ||||||
|     Makefile) |     Makefile) | ||||||
|   | |||||||
| @@ -1 +1,3 @@ | |||||||
|  | SUBDIRS = systemd | ||||||
|  |  | ||||||
| EXTRA_DIST = ipa.py | EXTRA_DIST = ipa.py | ||||||
|   | |||||||
| @@ -15,10 +15,6 @@ | |||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * GNU General Public License for more details. |  * 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. |  | ||||||
|  */ |  */ | ||||||
| """ | """ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,10 @@ | |||||||
| #!/usr/bin/env bash | #!/usr/bin/env bash | ||||||
| # jenkins build helper script for openbsc.  This is how we build on jenkins.osmocom.org | # jenkins build helper script for openbsc.  This is how we build on jenkins.osmocom.org | ||||||
|  | # | ||||||
|  | # environment variables: | ||||||
|  | # * WITH_MANUALS: build manual PDFs if set to "1" | ||||||
|  | # * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1") | ||||||
|  | # | ||||||
|  |  | ||||||
| if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then | if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then | ||||||
| 	echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !" | 	echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !" | ||||||
| @@ -14,19 +19,26 @@ deps="$base/deps" | |||||||
| inst="$deps/install" | inst="$deps/install" | ||||||
| export deps inst | export deps inst | ||||||
|  |  | ||||||
| mkdir "$deps" || true | osmo-clean-workspace.sh | ||||||
| rm -rf "$inst" |  | ||||||
|  |  | ||||||
|  | mkdir "$deps" || true | ||||||
| osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false | osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false | ||||||
|  |  | ||||||
| verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]") | verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]") | ||||||
|  |  | ||||||
| export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" | export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" | ||||||
| export LD_LIBRARY_PATH="$inst/lib" | export LD_LIBRARY_PATH="$inst/lib" | ||||||
|  | export PATH="$inst/bin:$PATH" | ||||||
|  |  | ||||||
| osmo-build-dep.sh libosmo-abis | osmo-build-dep.sh libosmo-abis | ||||||
| osmo-build-dep.sh libosmo-netif | osmo-build-dep.sh libosmo-netif | ||||||
|  |  | ||||||
|  | # Additional configure options and depends | ||||||
|  | CONFIG="" | ||||||
|  | if [ "$WITH_MANUALS" = "1" ]; then | ||||||
|  | 	CONFIG="--enable-manuals" | ||||||
|  | fi | ||||||
|  |  | ||||||
| set +x | set +x | ||||||
| echo | echo | ||||||
| echo | echo | ||||||
| @@ -37,11 +49,18 @@ set -x | |||||||
|  |  | ||||||
| cd "$base" | cd "$base" | ||||||
| autoreconf --install --force | autoreconf --install --force | ||||||
| ./configure $MGCP --enable-vty-tests --enable-external-tests | ./configure --enable-sanitize --enable-vty-tests --enable-external-tests --enable-werror $CONFIG | ||||||
| $MAKE $PARALLEL_MAKE | $MAKE $PARALLEL_MAKE | ||||||
| LD_LIBRARY_PATH="$inst/lib" $MAKE check \ | LD_LIBRARY_PATH="$inst/lib" $MAKE check \ | ||||||
|   || cat-testlogs.sh |   || cat-testlogs.sh | ||||||
| LD_LIBRARY_PATH="$inst/lib" \ | LD_LIBRARY_PATH="$inst/lib" \ | ||||||
|   DISTCHECK_CONFIGURE_FLAGS="$MGCP --enable-vty-tests --enable-external-tests" \ |   DISTCHECK_CONFIGURE_FLAGS="--enable-vty-tests --enable-external-tests $CONFIG" \ | ||||||
|   $MAKE distcheck \ |   $MAKE $PARALLEL_MAKE distcheck \ | ||||||
|   || cat-testlogs.sh |   || cat-testlogs.sh | ||||||
|  |  | ||||||
|  | if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then | ||||||
|  | 	make -C "$base/doc/manuals" publish | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | $MAKE $PARALLEL_MAKE maintainer-clean | ||||||
|  | osmo-clean-workspace.sh | ||||||
|   | |||||||
							
								
								
									
										137
									
								
								contrib/osmo-mgw.spec.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								contrib/osmo-mgw.spec.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | |||||||
|  | # | ||||||
|  | # spec file for package osmo-mgw | ||||||
|  | # | ||||||
|  | # Copyright (c) 2017, Martin Hauke <mardnh@gmx.de> | ||||||
|  | # | ||||||
|  | # All modifications and additions to the file contributed by third parties | ||||||
|  | # remain the property of their copyright owners, unless otherwise agreed | ||||||
|  | # upon. The license for this file, and modifications and additions to the | ||||||
|  | # file, is the same license as for the pristine package itself (unless the | ||||||
|  | # license for the pristine package is not an Open Source License, in which | ||||||
|  | # case the license is the MIT License). An "Open Source License" is a | ||||||
|  | # license that conforms to the Open Source Definition (Version 1.9) | ||||||
|  | # published by the Open Source Initiative. | ||||||
|  |  | ||||||
|  | # Please submit bugfixes or comments via http://bugs.opensuse.org/ | ||||||
|  | # | ||||||
|  |  | ||||||
|  | Name:           osmo-mgw | ||||||
|  | Version:        @VERSION@ | ||||||
|  | Release:        0 | ||||||
|  | Summary:        Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks | ||||||
|  | License:        AGPL-3.0-or-later AND GPL-2.0-or-later | ||||||
|  | Group:          Hardware/Mobile | ||||||
|  | URL:            https://osmocom.org/projects/osmo-mgw | ||||||
|  | Source:         %{name}-%{version}.tar.xz | ||||||
|  | BuildRequires:  automake >= 1.9 | ||||||
|  | BuildRequires:  libtool >= 2 | ||||||
|  | BuildRequires:  pkgconfig >= 0.20 | ||||||
|  | %if 0%{?suse_version} | ||||||
|  | BuildRequires:  systemd-rpm-macros | ||||||
|  | %endif | ||||||
|  | BuildRequires:  pkgconfig(libosmo-netif) >= 1.1.0 | ||||||
|  | BuildRequires:  pkgconfig(libosmocore) >= 1.6.0 | ||||||
|  | BuildRequires:  pkgconfig(libosmoctrl) >= 1.6.0 | ||||||
|  | BuildRequires:  pkgconfig(libosmogsm) >= 1.6.0 | ||||||
|  | BuildRequires:  pkgconfig(libosmovty) >= 1.6.0 | ||||||
|  | BuildRequires:  pkgconfig(libosmocoding) >= 1.6.0 | ||||||
|  | BuildRequires:  pkgconfig(libosmoabis) >= 1.2.0 | ||||||
|  | BuildRequires:  pkgconfig(libosmotrau) >= 1.2.0 | ||||||
|  | %{?systemd_requires} | ||||||
|  |  | ||||||
|  | %description | ||||||
|  | OsmoMGW is Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks. | ||||||
|  |  | ||||||
|  | %package -n libosmo-mgcp-client9 | ||||||
|  | Summary:        Osmocom's Media Gateway Control Protocol client library | ||||||
|  | Group:          System/Libraries | ||||||
|  |  | ||||||
|  | %description -n libosmo-mgcp-client9 | ||||||
|  | Osmocom's Media Gateway Control Protocol client library. | ||||||
|  |  | ||||||
|  | %package -n libosmo-mgcp-client-devel | ||||||
|  | Summary:        Development files for Osmocom's Media Gateway Control Protocol client library | ||||||
|  | Group:          Development/Libraries/C and C++ | ||||||
|  | Requires:       libosmo-mgcp-client9 = %{version} | ||||||
|  |  | ||||||
|  | %description -n libosmo-mgcp-client-devel | ||||||
|  | Osmocom's Media Gateway Control Protocol client librarary. | ||||||
|  |  | ||||||
|  | This subpackage contains libraries and header files for developing | ||||||
|  | applications that want to make use of libosmo-mgcp-client. | ||||||
|  |  | ||||||
|  | %package -n libosmo-mgcp-devel | ||||||
|  | Summary:        Development files for Osmocom's Media Gateway server library | ||||||
|  | Group:          Development/Libraries/C and C++ | ||||||
|  |  | ||||||
|  | %description -n libosmo-mgcp-devel | ||||||
|  | Osmocom's Media Gateway Control Protocol server library. | ||||||
|  |  | ||||||
|  | This subpackage contains libraries and header files for developing | ||||||
|  | applications that want to make use of libosmo-mgcp. | ||||||
|  |  | ||||||
|  | %prep | ||||||
|  | %setup -q | ||||||
|  |  | ||||||
|  | %build | ||||||
|  | echo "%{version}" >.tarball-version | ||||||
|  | autoreconf -fi | ||||||
|  | %configure \ | ||||||
|  |   --disable-static \ | ||||||
|  |   --docdir=%{_docdir}/%{name} \ | ||||||
|  |   --with-systemdsystemunitdir=%{_unitdir} | ||||||
|  |  | ||||||
|  | make %{?_smp_mflags} | ||||||
|  |  | ||||||
|  | %install | ||||||
|  | %make_install | ||||||
|  | find %{buildroot} -type f -name "*.la" -delete -print | ||||||
|  |  | ||||||
|  | %check | ||||||
|  | make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +) | ||||||
|  |  | ||||||
|  | %post   -n libosmo-mgcp-client9 -p /sbin/ldconfig | ||||||
|  | %postun -n libosmo-mgcp-client9 -p /sbin/ldconfig | ||||||
|  |  | ||||||
|  | %if 0%{?suse_version} | ||||||
|  | %preun | ||||||
|  | %service_del_preun osmo-mgw.service | ||||||
|  |  | ||||||
|  | %postun | ||||||
|  | %service_del_postun osmo-mgw.service | ||||||
|  |  | ||||||
|  | %pre | ||||||
|  | %service_add_pre osmo-mgw.service | ||||||
|  |  | ||||||
|  | %post | ||||||
|  | %service_add_post osmo-mgw.service | ||||||
|  | %endif | ||||||
|  |  | ||||||
|  | %files | ||||||
|  | %license COPYING | ||||||
|  | %doc AUTHORS README | ||||||
|  | %dir %{_docdir}/%{name}/examples | ||||||
|  | %dir %{_docdir}/%{name}/examples/osmo-mgw | ||||||
|  | %{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw.cfg | ||||||
|  | %{_docdir}/%{name}/examples/osmo-mgw/osmo-mgw-abis_e1.cfg | ||||||
|  | %{_bindir}/osmo-mgw | ||||||
|  | %{_unitdir}/osmo-mgw.service | ||||||
|  | %dir %{_sysconfdir}/osmocom | ||||||
|  | %config(noreplace) %{_sysconfdir}/osmocom/osmo-mgw.cfg | ||||||
|  |  | ||||||
|  | %files -n libosmo-mgcp-client9 | ||||||
|  | %{_libdir}/libosmo-mgcp-client.so.9* | ||||||
|  |  | ||||||
|  | %files -n libosmo-mgcp-client-devel | ||||||
|  | %{_libdir}/libosmo-mgcp-client.so | ||||||
|  | %{_libdir}/pkgconfig/libosmo-mgcp-client.pc | ||||||
|  | %dir %{_includedir}/osmocom | ||||||
|  | %dir %{_includedir}/osmocom/mgcp_client | ||||||
|  | %{_includedir}/osmocom/mgcp_client/*.h | ||||||
|  |  | ||||||
|  | %files -n libosmo-mgcp-devel | ||||||
|  | %dir %{_includedir}/osmocom | ||||||
|  | %dir %{_includedir}/osmocom/mgcp | ||||||
|  | %{_includedir}/osmocom/mgcp/*.h | ||||||
|  |  | ||||||
|  | %changelog | ||||||
							
								
								
									
										6
									
								
								contrib/systemd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								contrib/systemd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | EXTRA_DIST = osmo-mgw.service | ||||||
|  |  | ||||||
|  | if HAVE_SYSTEMD | ||||||
|  | systemdsystemunit_DATA = \ | ||||||
|  |   osmo-mgw.service | ||||||
|  | endif | ||||||
| @@ -1,11 +0,0 @@ | |||||||
| [Unit] |  | ||||||
| Description=OpenBSC MGCP |  | ||||||
|  |  | ||||||
| [Service] |  | ||||||
| Type=simple |  | ||||||
| Restart=always |  | ||||||
| ExecStart=/usr/bin/osmo-bsc_mgcp -s -c /etc/osmocom/osmo-bsc-mgcp.cfg |  | ||||||
| RestartSec=2 |  | ||||||
|  |  | ||||||
| [Install] |  | ||||||
| WantedBy=multi-user.target |  | ||||||
							
								
								
									
										11
									
								
								contrib/systemd/osmo-mgw.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contrib/systemd/osmo-mgw.service
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | [Unit] | ||||||
|  | Description=Osmocom Media Gateway (MGW) | ||||||
|  |  | ||||||
|  | [Service] | ||||||
|  | Type=simple | ||||||
|  | Restart=always | ||||||
|  | ExecStart=/usr/bin/osmo-mgw -s -c /etc/osmocom/osmo-mgw.cfg | ||||||
|  | RestartSec=2 | ||||||
|  |  | ||||||
|  | [Install] | ||||||
|  | WantedBy=multi-user.target | ||||||
							
								
								
									
										744
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										744
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,747 @@ | |||||||
|  | osmo-mgw (1.9.0) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   [ Harald Welte ] | ||||||
|  |   * TOS bits != DSCP | ||||||
|  |   * manual: don't define fig-bsc twice | ||||||
|  |   * switch from osmo_sock_set_dscp() to OSMO_SOCK_F_DSCP() | ||||||
|  |   * mgw: Add support for setting socket priority from VTY | ||||||
|  |   * manual: Include QoS chapter and add osmo-mgw specific example | ||||||
|  |  | ||||||
|  |   [ Keith ] | ||||||
|  |   * Add vty command 'show mgcp active' | ||||||
|  |   * Log some useful messages at ERROR/INFO instead of DEBUG | ||||||
|  |  | ||||||
|  |   [ Neels Hofmeyr ] | ||||||
|  |   * send DLCX only once | ||||||
|  |   * tweak termination DLCX log msg | ||||||
|  |   * add osmo_mgcpc_ep_ci_get_remote_rtp_info() | ||||||
|  |   * check_rtp_destin(): clarify log msg | ||||||
|  |   * mgcp_client_fsm delete: set mgcp_client as ctx, not NULL | ||||||
|  |   * mgcp_client_endpoint_fsm: on term, still let conns wait for DLCX OK | ||||||
|  |   * mgcp_client_fsm: add missing log_subsys | ||||||
|  |   * mgcp_client: add logging on received MGCP messages | ||||||
|  |  | ||||||
|  |   [ Philipp Maier ] | ||||||
|  |   * mgcp_network: fix implicit address loopback | ||||||
|  |   * mgcp_common, mgcp_udp_send: make parameter buf const | ||||||
|  |   * mgcp_network: refactor MGCP_DUMMY_LOAD | ||||||
|  |   * mgcp_client: drop nunnecessary else statement | ||||||
|  |   * mgcp_client: fix error handling in mgcp message generation | ||||||
|  |   * mgcp_protocol: fix loop that sends dummy RTP packets | ||||||
|  |   * mgw_main: fix loop that resets all endpoints | ||||||
|  |   * mgcp_trunk: drop ws line | ||||||
|  |   * mgcp_ratectr: drop ws line | ||||||
|  |   * mgcp_ratectr: fix sourcecode formatting | ||||||
|  |   * mgcp_trunk: add value string for trunk type. | ||||||
|  |   * mgcp_ratectr: refactor rate counter and set group name | ||||||
|  |   * mgcp_protocol: forward declare mgcp_endpoint | ||||||
|  |   * mgcp_endp: make wildcarded detection separate | ||||||
|  |   * mgcp_protocol: refactor request handler array | ||||||
|  |   * mgcp_trunk: use unsigned int instead of int as trunk_nr | ||||||
|  |   * mgcp_protocol: refactor MGCP request handling | ||||||
|  |   * mgcp_ratectr: add stats items to monitor trunk usage | ||||||
|  |   * mgcp_msg: add trunk parameter to mgcp_check_param for logging | ||||||
|  |   * mgcp_protocol: refactor function create_response_with_sdp | ||||||
|  |   * mgcp_protocol: add support for wildcarded DLCX | ||||||
|  |   * remove struct member wildcarded_req from struct mgcp_endpoint | ||||||
|  |   * mgcp_lient: remove unsubstantial FIXME note | ||||||
|  |   * mgcp_protocol: assert endp when it becomes mandatory | ||||||
|  |   * mgcp_trunk: check MGW domain name earlier | ||||||
|  |   * mgcp_ratectr: add stats items to monitor trunk usage | ||||||
|  |   * mgcp_client: fix typo Initalize -> Initialize | ||||||
|  |   * mgcp_protocol: get rid of policy_cb and change_cb | ||||||
|  |   * mgcp_ratectr: do not set talloc destructor on library allocated item | ||||||
|  |   * mgcp_client: allow to reset endpoints on startup | ||||||
|  |   * mgcp_client_vty: remove unnecessary checks | ||||||
|  |   * mgcp_client: do not print (null) when address is ANY | ||||||
|  |   * mgcp_client: refactor function init_socket | ||||||
|  |   * libosmo-mgcp-client: extend the mgcp_client for MGW pooling | ||||||
|  |   * mgcp_client_vty: add missing docstrings | ||||||
|  |   * mgcp_client_vty: add OSMO_ASSERT on pool parameter | ||||||
|  |   * mgcp_client_vty: fix docstrings for mgw-pool | ||||||
|  |   * mgcp_client_vty: cosmetic: doc string should terminated with \n | ||||||
|  |   * mgcp_client_vty: fixing docstring | ||||||
|  |   * mgcp_ratectr: remove unusued rate counters | ||||||
|  |   * mgcp_client: fix typo in doxygen comment | ||||||
|  |   * mgcp_client: add MGW name as logging context | ||||||
|  |  | ||||||
|  |   [ Pau Espin Pedrol ] | ||||||
|  |   * Use new stat item/ctr getter APIs | ||||||
|  |   * constify arg in addr_is_any() | ||||||
|  |   * Define patch_ssrc as bool type | ||||||
|  |   * constify some function arg pointers | ||||||
|  |   * mgcp_network.c: Reorder some functions in file | ||||||
|  |   * mgcp_send_dummy: Check RTP destination is available before attempt tx | ||||||
|  |   * mgw: rx CRCX: Avoid sending dummy rtp if remote address not provided | ||||||
|  |   * Fail rx MDCX sendrecv with invalid remote addr | ||||||
|  |   * Take into account Marker bit when patching RTP stream | ||||||
|  |   * Use DLMGCP instead of DLGLOBAL in log lines | ||||||
|  |   * Fix attribute parsing on gcc 11.1.0 | ||||||
|  |  | ||||||
|  |   [ neels ] | ||||||
|  |   * Revert "mgcp_ratectr: add stats items to monitor trunk usage" | ||||||
|  |  | ||||||
|  |   [ Daniel Willmann ] | ||||||
|  |   * contrib/jenkins: Use ASAN for osmo-mgw | ||||||
|  |  | ||||||
|  |   [ Oliver Smith ] | ||||||
|  |   * mgcp_client_vty: add missing NO_STR | ||||||
|  |   * Revert "Turn some compiler warnings into errors" | ||||||
|  |  | ||||||
|  |   [ Eric ] | ||||||
|  |   * add vscode stuff to gitignore | ||||||
|  |   * configure.ac: fix maybe-uninitialized for clang | ||||||
|  |   * fix missing includes and forward declarations | ||||||
|  |   * rename do_retransmission | ||||||
|  |   * mgcp_sdp: fix potential leak | ||||||
|  |   * stats: make sanitizers happy | ||||||
|  |   * libosmo-mgcp: cleanup audio codex alloc | ||||||
|  |   * libosmo-mgcp: atomic rate counter group indexes | ||||||
|  |   * libosmo-mgcp: do not use the default msgb talloc context | ||||||
|  |   * globally lock the portrange when trying to grab a port to prep for multithreading | ||||||
|  |   * embed strings into structs | ||||||
|  |   * adjust talloc context | ||||||
|  |   * endp: do not cache cfg pointer | ||||||
|  |   * add modified .clang-format | ||||||
|  |   * rename strip_epname and find_specific_endpoint and make them available | ||||||
|  |   * fix mgcp_conn_free_all | ||||||
|  |   * fix up some docstrings that lost their dot | ||||||
|  |  | ||||||
|  |   [ Vadim Yanitskiy ] | ||||||
|  |   * libosmo-mgcp: use OSMO_STRLCPY_ARRAY in mgcp_codec_add() | ||||||
|  |  | ||||||
|  |  -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 16 Nov 2021 16:59:21 +0100 | ||||||
|  |  | ||||||
|  | osmo-mgw (1.8.1) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * attempt to fix RPM spec file after recent soversion bump | ||||||
|  |  | ||||||
|  |  -- Harald Welte <laforge@osmocom.org>  Wed, 24 Feb 2021 10:56:17 +0100 | ||||||
|  |  | ||||||
|  | osmo-mgw (1.8.0) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   [ Harald Welte ] | ||||||
|  |   * Fix number of endpoints of default trunk | ||||||
|  |   * Add CTRL interface to osmo-mgw | ||||||
|  |   * Update per-trunk global packet/byte counters in real-time | ||||||
|  |   * remove accidential TODO-RELEASE entry | ||||||
|  |   * mgcp_protocol: Avoid code duplication between virtual + other trunks | ||||||
|  |   * osmo-mgw.spec.in: Use %config(noreplace) to retain current config file | ||||||
|  |   * mgcp_client_init(): consider "talloc returns NULL" case | ||||||
|  |   * mgcp_client_pending_add(): Consider "talloc returns NULL" case | ||||||
|  |   * libosmo-mgcp-client: fix memleak in case if no response is received | ||||||
|  |   * debian/control + SPEC: Add missing build dependency to libosmo-abis | ||||||
|  |   * osmo-mgw.spec.in: Fix dependency to libosmoabis | ||||||
|  |   * osmo-mgw.spec.in: Add missing dependency to libosmotrau | ||||||
|  |   * Add example osmo-mgw configuration file for Abis/E1 | ||||||
|  |   * use osmo_fd_setup() whenever applicable | ||||||
|  |  | ||||||
|  |   [ Neels Hofmeyr ] | ||||||
|  |   * fix vty dump_trunk: start from zero, do not omit first CONN | ||||||
|  |   * allow larger MGCP client wqueue: 10 -> 1024 | ||||||
|  |   * add osmo-mgw --vty-ref-xml: dump VTY ref XML to stdout | ||||||
|  |   * manuals: generate vty reference xml at build time | ||||||
|  |   * refactor: use msgb to receive, pass and send RTP packets | ||||||
|  |   * change timer T2427001 to X2427 | ||||||
|  |  | ||||||
|  |   [ Philipp Maier ] | ||||||
|  |   * doc: do not bind osmo-mgw to random ip-address | ||||||
|  |   * cosmetic: remove excess newlines | ||||||
|  |   * mgcp: remove unused callback pointer | ||||||
|  |   * mgcp: find better locations for LOGPCONN and LOGPENDP | ||||||
|  |   * vty: fix unreachable code (error msg on trunk alloc fail) | ||||||
|  |   * mgcp_vty: fix indentation | ||||||
|  |   * osmo-mgw: rename struct mgcp_trunk_config and symbol tcfg | ||||||
|  |   * mgcp_vty: fix indentation in VTY config write | ||||||
|  |   * osmo-mgw: refactor endpoint and trunk handling | ||||||
|  |   * mgcp_trunk: remove audio_name and audio_payload | ||||||
|  |   * ratectr: move rate counter definitions into mgcp_ratectr.h | ||||||
|  |   * cosmetic: fix doxygen | ||||||
|  |   * cosmetic: fix doxygen | ||||||
|  |   * cosmetic: fix doxygen for mgcp_cleanup_rtp_bridge_cb() | ||||||
|  |   * trunk: get rid of virt_trunk pointer | ||||||
|  |   * cosmetic: remove excess space | ||||||
|  |   * endp: move endpoint name generation into mgcp_endp.c | ||||||
|  |   * endp: add name generator function for E1 endpoints | ||||||
|  |   * mgcp_client: add docstring for mgcp_client_rtpbridge_wildcard() | ||||||
|  |   * mgcp_trunk: fix docstring for mgcp_trunk_alloc() | ||||||
|  |   * mgcp_osmux: remove unused define constants | ||||||
|  |   * mgcp_trunk: move enum mgcp_trunk_type to mgcp_trunk.h | ||||||
|  |   * mgcp_trunk: use enum type for trunk type variable | ||||||
|  |   * trunk: parse E1 trunk number | ||||||
|  |   * endp: add typeset for e1-endpoints | ||||||
|  |   * mgcp_conn: move struct mgcp_conn mgcp_conn.h | ||||||
|  |   * mgcp_internal: remove forward declaration struct mgcp_endpoint_type | ||||||
|  |   * endp: add E1 endpoint interlocking | ||||||
|  |   * endp: require domain name also for E1 endpoints | ||||||
|  |   * mgcp_trunk: pick trunk by number and type | ||||||
|  |   * mgcp_client: add function to generate e1-endpoint names | ||||||
|  |   * mgcp_endp: use define constant to define max number of E1 subslots | ||||||
|  |   * mgcp_endp.c: cosmetic: fix sourcecode formatting | ||||||
|  |   * mgcp_trunk: remove double check | ||||||
|  |   * mgcp_test: do not access endpoint array elements directly | ||||||
|  |   * mgcp_vty: fix endpoint number configuration | ||||||
|  |   * mgcp_test: remove trunk2 from unit-test | ||||||
|  |   * mgcp_trunk: use talloc_zero_array instead of _talloc_zero_array | ||||||
|  |   * mgcp_protocol: remove unused variable | ||||||
|  |   * mgcp_e1: finish E1 support, add E1 support from libosmoabis | ||||||
|  |   * get rid of mgcp_internal.h | ||||||
|  |   * mgcp_ratectr: fix comments in header file | ||||||
|  |   * mgcp_e1: use return value of e1inp_line_update() | ||||||
|  |   * mgcp_protocol: log when endpoint is unavailable | ||||||
|  |   * cosmetic: add missing new-line | ||||||
|  |   * mgcp_trunk: drop "trunk 0" limitation | ||||||
|  |   * mgcp_e1: make E1 ts initalization more debugable | ||||||
|  |   * mgcp_e1: remove unused struct member trunk->e1.line | ||||||
|  |   * mgcp_endp: use NUM_E1_TS from e1_input.h | ||||||
|  |   * mgcp_trunk: increase default number of virtual endpoints | ||||||
|  |   * cosmetic: mgcp_client_fsm: change error message. | ||||||
|  |   * mgcp_vty: remove remains of loopback functionality | ||||||
|  |   * mgcp_vty: deprecate bind early command | ||||||
|  |   * mgcp_e1: do not expose function  mgcp_e1_init() | ||||||
|  |   * mgcp_vty: add user attributes to configuration commands | ||||||
|  |   * overview: update section limitations. | ||||||
|  |   * overview: fix graph "OsmoMGW used with OsmoBSC" | ||||||
|  |   * overview: fix graph "OsmoMGW used with OsmoMSC" | ||||||
|  |   * overview: add graph to show E1 integration | ||||||
|  |   * configuration: remove hint towards trunk 0 limit | ||||||
|  |   * configuration: drop note about lackin E1 support | ||||||
|  |   * configuration: add note that changes to trunks need a restart | ||||||
|  |   * mgcp_vty: fix config write for trunk 0 | ||||||
|  |   * mgcp_vty: add missing VTY commands for E1 trunks | ||||||
|  |   * mgcp_trunk: get rid of magic numbers for E1 slots | ||||||
|  |   * configuration: add section about E1 trunks | ||||||
|  |   * usermanual: add chapter about mgcp endpoints | ||||||
|  |   * mgcp_client: get rid of magic numbers for E1 slots | ||||||
|  |  | ||||||
|  |   [ Eric ] | ||||||
|  |   * tests: dlopen does not imply availability of dlsym.. | ||||||
|  |   * configure.ac: fix libtool issue with clang and sanitizer | ||||||
|  |  | ||||||
|  |   [ Alexander Chemeris ] | ||||||
|  |   * vty: Prepend VTY output of counters for better visual separation. | ||||||
|  |   * counters: Implement more useful counters. | ||||||
|  |   * mgcp_network: Fix a typo in the comment bahviour -> behaviour | ||||||
|  |   * rtp_bridge: Demote a chatty ERROR log message to DEBUG level. | ||||||
|  |  | ||||||
|  |   [ Pau Espin Pedrol ] | ||||||
|  |   * Use OSMO_FD_* instead of deprecated BSC_FD_* | ||||||
|  |   * Support setting rt-prio and cpu-affinity mask through VTY | ||||||
|  |   * cosmetic: Rename main talloc ctx | ||||||
|  |   * mgcp-client: Support IPv6 in osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr() implementation | ||||||
|  |   * mgcp-client: Fix trailing whitespace in mgcp_client_fsm.h | ||||||
|  |   * mgcp_client: Use INET6_ADDRSTRLEN to store addresses in str format | ||||||
|  |   * mgcp_client: Allow submitting and parsing IPv6 addr in SDP | ||||||
|  |   * mgcp_client: Allow setting IPv6 addresses | ||||||
|  |   * mgcp_client: Make MGCP_CLIENT_LOCAL_ADDR_DEFAULT IPv6 compatible | ||||||
|  |   * mgcp_client: Support validating IPv6 addresses in CRCX and MDCX commands | ||||||
|  |   * mgcp_client: Deprecate unused IPv4-only API | ||||||
|  |   * mgcp_client: copy back Connection Information from MDCX ACK | ||||||
|  |   * mgw: Fix mgcp_rtp_end field description comment | ||||||
|  |   * mgw: Initial IPv6 support | ||||||
|  |   * mgw: Introduce VTY cmd 'rtp bind-ip-v6' command | ||||||
|  |   * mgw: Find and store RTP conn local_addr once during CRCX handling | ||||||
|  |   * mgw: Announce and rebind new local address if change required during MDCX | ||||||
|  |   * mgw: osmux: Avoid sending packets on recvonly connection | ||||||
|  |   * mgw: Release endpoint after last conn times out | ||||||
|  |   * mgw: osmux: Fix conn watchdog timeout not updated | ||||||
|  |   * mgw: Don't be case-sensitive when parsing X-Osmo-IGN param | ||||||
|  |   * cosmetic: Fix typo in comment | ||||||
|  |   * mgw: Avoid logging notice message each time we receive nt param in LCO | ||||||
|  |   * mgw: Fix return value documentation for API mgcp_verify_call_id | ||||||
|  |   * cosmetic: Fix typo in comment | ||||||
|  |   * contrib/jenkins: Enable parallel make in make distcheck | ||||||
|  |   * .gitignore: Ignore new autofoo tmp files | ||||||
|  |   * tests: Replace deprecated API log_set_print_filename | ||||||
|  |  | ||||||
|  |   [ Oliver Smith ] | ||||||
|  |   * contrib: import RPM spec | ||||||
|  |   * contrib: integrate RPM spec | ||||||
|  |   * Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in | ||||||
|  |   * contrib/jenkins: don't build osmo-gsm-manuals | ||||||
|  |   * configure.ac: set -std=gnu11 | ||||||
|  |  | ||||||
|  |   [ Vadim Yanitskiy ] | ||||||
|  |   * libosmo-mgcp-client: fix use-after-free in mgcp_client_tx() | ||||||
|  |   * libosmo-mgcp-client: fix use-after-free in mgcp_msg_gen() | ||||||
|  |   * libosmo-mgcp: fix unsigned compared against 0 in mgcp_trunk_by_name() | ||||||
|  |   * libosmo-mgcp: fix unused extra argument to printf() in add_fmtp() | ||||||
|  |   * libosmo-mgcp: always check result of msgb_printf() in add_fmtp() | ||||||
|  |   * libosmo-mgcp-client: mgcp_client_tx(): return rc on error | ||||||
|  |   * debian/control: change maintainer to the Osmocom team / mailing list | ||||||
|  |   * vty: use install_lib_element() and install_lib_element_ve() | ||||||
|  |   * main: add --vty-ref-mode, use vty_dump_xml_ref_mode() | ||||||
|  |   * fixup mgcp_trunk: increase default number of virtual endpoints | ||||||
|  |  | ||||||
|  |   [ Alexander Couzens ] | ||||||
|  |   * configure.ac: require libosmoabis + libosmotrau >= 1.0.0 | ||||||
|  |  | ||||||
|  |  -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 23 Feb 2021 18:28:45 +0100 | ||||||
|  |  | ||||||
|  | osmo-mgw (1.7.0) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   [ Neels Hofmeyr ] | ||||||
|  |   * rename codecs_cmp() to codecs_same() | ||||||
|  |   * mgcp_codec: constify 'param' arg | ||||||
|  |   * fix crashes: don't assert on incoming RTP packet size | ||||||
|  |   * mgcp_send(): stop looping on conversion error | ||||||
|  |   * mgcp_codec: split codec_free() off of codec_init() | ||||||
|  |   * fix memleak: actually free strings in mgcp_codec_reset_all() | ||||||
|  |   * mgcp_test: extend / rewrite test_mgcp_codec_pt_translate() | ||||||
|  |   * test_mgcp_codec_pt_translate(): more tests | ||||||
|  |   * differentiate AMR octet-aligned=0 vs =1 | ||||||
|  |   * ptmap: implicitly match  '/8000' and '/8000/1' | ||||||
|  |   * mgcp_codec: codec_set(): log about all possible errors | ||||||
|  |   * mgcp_codec_add: fix audio_name size check | ||||||
|  |   * explicitly free codecs in mgcp_rtp_conn_cleanup() | ||||||
|  |   * tweak mgcp_parse_audio_ptime_rtpmap() | ||||||
|  |   * SDP: store all ptmap entries | ||||||
|  |   * mgcp_client_fsm cleanup: Do not assert on DLCX failure | ||||||
|  |   * clear pending requests on MGCP failure | ||||||
|  |   * client: endp fsm: add notify struct, prep for cancel-notify | ||||||
|  |   * client: endp fsm: clear ci[] before dispatching DLCX success | ||||||
|  |   * client: endp fsm: allow cancelling a notify event | ||||||
|  |   * client: endp fsm: add osmo_mgcpc_ep_ci_ep() | ||||||
|  |   * accept MGCP without SDP | ||||||
|  |   * fix use-after-free: require new fsm deferred dealloc, check for term | ||||||
|  |  | ||||||
|  |   [ Pau Espin Pedrol ] | ||||||
|  |   * mgcp_test: Correctly release all endpoints allocated | ||||||
|  |   * mgw: Allocate mgcp_conn instance under tcfg->endpoints | ||||||
|  |  | ||||||
|  |   [ Harald Welte ] | ||||||
|  |   * manual: Fix copy+paste error | ||||||
|  |   * mgcp_client: Check for osmo_fsm_register() error return value | ||||||
|  |   * Move fsm_mgcp_client regstration to __attribute__((contructor)) | ||||||
|  |   * exit(2) on unsupported positional arguments on command line | ||||||
|  |  | ||||||
|  |   [ Oliver Smith ] | ||||||
|  |   * osmoappdesc.py: switch to python 3 | ||||||
|  |  | ||||||
|  |  -- Pau Espin Pedrol <pespin@sysmocom.de>  Fri, 03 Jan 2020 13:35:09 +0100 | ||||||
|  |  | ||||||
|  | osmo-mgw (1.6.0) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   [ Oliver Smith ] | ||||||
|  |   * Cosmetic: fix spaces/tabs in mgcp_requests[] | ||||||
|  |   * Inactive connection cleanup (disabled by default) | ||||||
|  |   * debian: create -doc subpackage with pdf manuals | ||||||
|  |   * vty: allow 0 as conn-timeout to disable it | ||||||
|  |   * vty: update desc of conn-timeout | ||||||
|  |   * manuals: update VTY documentation | ||||||
|  |   * "make dist" fix for: no rule to make mgcp_common.h | ||||||
|  |   * contrib/jenkins.sh: run "make maintainer-clean" | ||||||
|  |  | ||||||
|  |   [ Philipp Maier ] | ||||||
|  |   * Add option to GSM HR frames to RFC5593 representation | ||||||
|  |   * mgcp_sdp: cosmetic: correct typo in comment | ||||||
|  |   * mgco_client: cosmetic: remove excess whitespace | ||||||
|  |   * mgcp_sdp: mgcp_sdp.c does not include own header file | ||||||
|  |   * mgcp_sdp: untangle parsing of a= parameters | ||||||
|  |   * mgcp_network: use mgcp_rtp_codec in downlink format callback | ||||||
|  |   * AMR: Add function to convert between bw-effient and octet aligned mode | ||||||
|  |  | ||||||
|  |   [ Neels Hofmeyr ] | ||||||
|  |   * log: don't spam with "can not patch PT" messages | ||||||
|  |   * mgcp client: allow setting conn mode for CRCX and MDCX | ||||||
|  |   * make codec_table public as osmo_mgcpc_codec_names | ||||||
|  |   * move MGW endpoint FSM from osmo-bsc to here | ||||||
|  |   * fix: multiple initial CRCX | ||||||
|  |   * constify map_codec_to_pt() ptmap arg | ||||||
|  |  | ||||||
|  |   [ Pau Espin Pedrol ] | ||||||
|  |   * mgcp-client: whitespace cleanup in mgcp_client.c | ||||||
|  |   * mgcp-client: Sanitize implementation of mgcp_client_rtpbridge_wildcard | ||||||
|  |   * libosmo-mgcp: Use trunk type during endpoint allocation | ||||||
|  |   * cosmetic: Fix typos in comment | ||||||
|  |   * cosmetic: tests: mgcp_client_test: clean trailing whitespace | ||||||
|  |   * mgcp_msg: Log faulty line on Osmux parsing error | ||||||
|  |   * cosmetic: handle_modify_con: Fix indentation level | ||||||
|  |   * Introduce log fmt helpers LOGPENDP and LOGPCONN | ||||||
|  |   * create_response_with_sdp: Fix inclusion of X-Osmux | ||||||
|  |   * osmux: Cleanup of CID alloc pool APIs | ||||||
|  |   * mgcp_osmux.c: osmux_enable_endpoint: Fix incorrect return check | ||||||
|  |   * osmux: Log osmux socket during osmux_init | ||||||
|  |   * mgcp-cli: Change osmo_mgcpc_ep_fsm name to avoid collision with old osmo-bsc | ||||||
|  |   * osmux: Document func and return different rc upon osmux init failure | ||||||
|  |   * osmux: Fix reception of legacy dummy payloads | ||||||
|  |   * osmux: Use LOGPCONN in several log calls | ||||||
|  |   * cosmetic: mgcp_udp_send: Document port param is in network byte order | ||||||
|  |   * cosmetic: osmux: Document network byte order in port variable | ||||||
|  |   * vty: Allow enabling Osmux | ||||||
|  |   * osmux: Allocate CID during CRCX | ||||||
|  |   * osmux: Introduce mgcp_conn_rtp_is_osmux() helper | ||||||
|  |   * osmux: Mark conn_rtp->type as osmux during CRCX | ||||||
|  |   * mgcp-cli: Allow submitting X-Osmux on CRCX request | ||||||
|  |   * mgcp-cli: Parse X-Osmux on CRCX response | ||||||
|  |   * osmux: Handle Osmux MGCP extension in MDCX messages | ||||||
|  |   * mgcp-cli: Allow submitting X-Osmux on MDCX request | ||||||
|  |   * mgcp-cli: Parse X-Osmux on MDCX response | ||||||
|  |   * osmux: Drop unneeded OSMUX_STATE_NEGOTIATING | ||||||
|  |   * mgcp-cli: endpoint_fsm: Add API to retrieve Osmux CID from MGW | ||||||
|  |   * osmux: Provide correct local port during mgcp resp | ||||||
|  |   * osmux: Use remote port to send osmux frames | ||||||
|  |   * osmux: Improve logging around osmux enabling events | ||||||
|  |   * osmux: Delay osmux enable of conn until remote addr is configured by MDCX | ||||||
|  |   * osmux: Redo read/write osmux glue code to have data routed correctly | ||||||
|  |   * mgw, mgcp-li: Handle X-Osmux param name as case insensitive | ||||||
|  |   * osmux: Use DUMMY ft msg as per Osmux spec | ||||||
|  |   * osmux: Fix loopback for Osmux connections | ||||||
|  |   * osmux: Fix CID release for non-enabled connections | ||||||
|  |   * osmux: Fix hardcoded rtp payload_type 98 in osmux conn | ||||||
|  |   * mgcp-cli: Validate osmux cid value during mgcp_msg_gen | ||||||
|  |   * mgw: Support uppercase LCO options | ||||||
|  |   * mgw: Support lowercase header parameters | ||||||
|  |   * mgcp-cli: Support lowercase header parameters | ||||||
|  |   * mgw: Support receiving uppercase connection mode | ||||||
|  |   * mgw: Support receiving lowercase LCO codec | ||||||
|  |   * mgw: Make check of duplicated LCO fields case insensitive | ||||||
|  |   * mgw: Allow receiving lowercase MGCP header keyword | ||||||
|  |   * mgw: Allow receiving lowercase X-Osmo-Ign Callid field | ||||||
|  |   * mgw: Allow receiving uppercase noanswer keyword | ||||||
|  |   * doc: X-Osmo-IGN: small formatting and typo fixes | ||||||
|  |   * doc: Add Osmux documentation to OsmoMGW User Manual | ||||||
|  |   * Catch unsigned integer MGCP parsing errors with strtoul | ||||||
|  |   * Fix return variable of strtoul() | ||||||
|  |   * Remove undefined param passed to {logging,osmo_stats}_vty_add_cmds | ||||||
|  |   * configure.ac: Require libosmo-netif 0.6.0 | ||||||
|  |  | ||||||
|  |   [ Alexander Couzens ] | ||||||
|  |   * mgcp_internal: LOGPENDP: ensure *endp is not NULL | ||||||
|  |  | ||||||
|  |   [ Harald Welte ] | ||||||
|  |   * handle NULL return of rate_ctr_group_alloc() | ||||||
|  |   * update .gitignore | ||||||
|  |   * mgcp_sdp: Don't check if an unsigned int is below 0 | ||||||
|  |  | ||||||
|  |   [ Vadim Yanitskiy ] | ||||||
|  |   * configure.ac: drop useless check for -fvisibility=hidden | ||||||
|  |  | ||||||
|  |   [ Daniel Willmann ] | ||||||
|  |   * manuals: Add script to regenerate vty/counter documentation | ||||||
|  |   * manuals: Update vty/counter documentation | ||||||
|  |  | ||||||
|  |   [ Hoernchen ] | ||||||
|  |   * turn -Werror=null-dereference into a warning | ||||||
|  |  | ||||||
|  |  -- Pau Espin Pedrol <pespin@sysmocom.de>  Wed, 07 Aug 2019 16:52:58 +0200 | ||||||
|  |  | ||||||
|  | osmo-mgw (1.5.0) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   [ Pau Espin Pedrol ] | ||||||
|  |   * gitignore: Filter *.pc | ||||||
|  |   * configure: Find correct libgsm's gsm.h header | ||||||
|  |   * vty: Fix typo writing bts-jitter-buffer-delay-{min,max} | ||||||
|  |   * Remove libosmo-legacy-mgcp and osmo-bsc-mgcp | ||||||
|  |   * debian: Remove dangling symlink to osmo-bsc-mgcp.service | ||||||
|  |   * Install systemd services with autotools | ||||||
|  |   * Install sample cfg file to /etc/osmocom | ||||||
|  |   * mgcp: Fix osmux_cid_bitmap static array size calculation | ||||||
|  |   * mgcp_osmux: Use define to calculate rtp_ssrc_winlen | ||||||
|  |   * osmux: Avoid initing output without enabling osmux | ||||||
|  |   * mgcp: Log endpoint nr consistently as hex | ||||||
|  |   * osmux_send_dummy: Avoid logging incorrectly and sending if osmux not enabled | ||||||
|  |   * osmux: Don't process regular osmux frames if disabled by cfg | ||||||
|  |   * osmux: Move parse_cid of legacy dummy frames to own function | ||||||
|  |   * osmux: Make func handling dummy frames independent of endp type | ||||||
|  |   * osmux: allow enabling osmux only on correct activating state | ||||||
|  |   * osmux: Improve checks around activating and using enabled osmux | ||||||
|  |   * osmux.h: Document enum osmux_state | ||||||
|  |   * osmux: Avoid processing further frames if conn not found | ||||||
|  |  | ||||||
|  |   [ Philipp Maier ] | ||||||
|  |   * mgcp_client_fsm: allow ptmap in mgcp_client_fsm as well | ||||||
|  |   * mgcp_network: translate payload type numbers in RTP packets | ||||||
|  |   * mgcp_client: use IETF source port as for MGCP | ||||||
|  |   * mgcp_client: increment local port number when port is in use | ||||||
|  |   * mgcp_test: release endpoints after use | ||||||
|  |   * network: do not patch PT of RTCP packets | ||||||
|  |   * network: check packets before further processing | ||||||
|  |   * Cosmetic: remove misplaced line break | ||||||
|  |   * mgcp_sdp: restructure mgcp_write_response_sdp() (rtpmap) | ||||||
|  |   * mgcp_sdp: restructure mgcp_write_response_sdp() (audio) | ||||||
|  |   * mgcp_client: check local port only once | ||||||
|  |   * mgcp_client_fsm: switch to MGCP_CONN_RECV_SEND in add_audio() | ||||||
|  |   * mgcp_protocol: increase buffer space for codec name in LCO | ||||||
|  |   * osmo-mgw: Add vty reference manual | ||||||
|  |  | ||||||
|  |   [ Neels Hofmeyr ] | ||||||
|  |   * fix handling of "Wrong domain name" error | ||||||
|  |   * interpret domain '*' as 'allow any domain' | ||||||
|  |   * cosmetic: log: fix "CallIDs does not match" | ||||||
|  |   * fix 3G hack: allow any IP for loopback and 0.0.0.0 | ||||||
|  |   * cosmetic: drop code dup in mgcp_client_fsm.c CRCX | ||||||
|  |   * add X-Osmo-IGN MGCP header to ignore CallID | ||||||
|  |   * X-Osmo-IGN: rather parse items by token, not char | ||||||
|  |   * mgcp_test: fix log of conn_id presence | ||||||
|  |   * mgcp_test: fix get_conn_id_from_response() CI length | ||||||
|  |   * mgcp_client_test: cosmetically re-arrange reply_to() args | ||||||
|  |   * mgcp_client_test: use "\r\n\r\n" instead of "\n\n" | ||||||
|  |   * mgcp_client_test: also verify received conn_id | ||||||
|  |   * mgcp_client_test: test long conn_id | ||||||
|  |   * mgcp_client: error on too long conn id | ||||||
|  |   * mgcp_common: rename to MGCP_CONN_ID_MAXLEN | ||||||
|  |   * doc: fix mgcp_verify_ci() return val doc | ||||||
|  |   * mgcp_verify_ci(): return meaningful error codes | ||||||
|  |   * fix mgcp_verify_ci(): off-by-one in max len check | ||||||
|  |   * generate shorter 'I:' conn IDs | ||||||
|  |   * mgcp_conn_get(): compare conn Id ('I:') case insensitively | ||||||
|  |   * mgcp_conn_get(): match conn Id ('I:') despite leading zeros | ||||||
|  |   * cosmetic: mgcp_test: fix get_conn_id_from_response() | ||||||
|  |   * comment: indicate struct type for mgcp_endpoint.conns | ||||||
|  |   * log: avoid logging early media as error | ||||||
|  |   * fix osmo-mgw -s; fixes osmo-mgw.service using -s | ||||||
|  |   * Importing history from osmo-gsm-manuals.git | ||||||
|  |   * OsmoMGW: update VTY reference | ||||||
|  |   * OsmoMGW: document the 'X-Osmo-IGN' MGCP extension | ||||||
|  |   * mgw: update vty reference | ||||||
|  |   * drop/replace very weird logging in mgcp_client.c | ||||||
|  |   * check_rtp: on IP:port errors, log the IP and port | ||||||
|  |   * osmo-mgw: err-log: include expected domain name | ||||||
|  |   * mgcp_client_vty: fix missing talloc_free | ||||||
|  |   * mgcp_client: drop a bunch of dead code | ||||||
|  |   * mgcp_client: logging tweaks | ||||||
|  |   * mgcp_client: make domain part of endpoint configurable | ||||||
|  |   * mgcp_client: tweak some log levels INFO -> {DEBUG,ERROR} | ||||||
|  |  | ||||||
|  |   [ Stefan Sperling ] | ||||||
|  |   * add VTY commands which show specific mgcp endpoints | ||||||
|  |   * add MGCP CRCX command statistics to osmo-mgw | ||||||
|  |   * show RTP TX/RX stats in 'mgcp show stats' output | ||||||
|  |   * use local variable for rate counters in handle_create_con() | ||||||
|  |   * add more mgcp crxc error counters | ||||||
|  |   * add MDCX command statistics to osmo-mgw | ||||||
|  |   * add aggregated rtp connection stats to osmo-mgw | ||||||
|  |   * add DLCX command statistics to osmo-mgw | ||||||
|  |  | ||||||
|  |   [ Harald Welte ] | ||||||
|  |   * debian/rules: Don't overwrite .tarball-version | ||||||
|  |   * check_rtp_origin(): Don't memcmp sockadd_in and in_addr | ||||||
|  |   * check_rtp_origin(): Avoid using memcmp for comparing integer types | ||||||
|  |   * vty-ref: Update URI of docbook 5.0 schema | ||||||
|  |  | ||||||
|  |   [ Daniel Willmann ] | ||||||
|  |   * mgw: Add new VTY reference | ||||||
|  |   * Add initial OsmoMGW manual | ||||||
|  |  | ||||||
|  |   [ Oliver Smith ] | ||||||
|  |   * build manuals moved here from osmo-gsm-manuals.git | ||||||
|  |   * jenkins.sh: remove leftover MGCP env variable | ||||||
|  |   * Fix DISTCHECK_CONFIGURE_FLAGS override | ||||||
|  |   * contrib/jenkins.sh: build and publish manuals | ||||||
|  |   * contrib: fix makedistcheck with disabled systemd | ||||||
|  |  | ||||||
|  |  -- Harald Welte <laforge@gnumonks.org>  Sun, 20 Jan 2019 15:02:18 +0100 | ||||||
|  |  | ||||||
|  | osmo-mgw (1.4.0) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   [ Philipp Maier ] | ||||||
|  |   * network: independently initalize state->out_stream | ||||||
|  |   * stats: use libosmocore rate counter for in/out_stream.err_ts_counter | ||||||
|  |   * mgcp_sdp: correct apidoc of mgcp_parse_sdp_data | ||||||
|  |   * vty: clean up rtp port-range command | ||||||
|  |   * sdp: remove unused alt_codec field from struct mgcp_rtp_end | ||||||
|  |   * sdp: remove circular inclusion | ||||||
|  |   * protocol: Try whole port range on port allocation | ||||||
|  |   * client: do not start connections in loopback mode | ||||||
|  |   * mgcp_network: do not log destination invalid ip/port as error | ||||||
|  |   * cosmetic: fix log output | ||||||
|  |   * conn: call talloc_free before setting the pointer to NULL | ||||||
|  |   * protocol: do not change LCO, when no LCO are present | ||||||
|  |   * protocol: reject illegal lco options | ||||||
|  |   * cosmetic: fix typo | ||||||
|  |   * mgw: clean up codec negotiation (sdp) | ||||||
|  |   * client: add features to generate and parse codec information | ||||||
|  |   * mgcp_internal: remove unused struct member | ||||||
|  |   * stats: replace packet statistic counters with libosmocore rate counters | ||||||
|  |   * stat+vty: fix printing of rate counter values | ||||||
|  |   * protocol: prevent unnecessary null pointer deref | ||||||
|  |  | ||||||
|  |   [ Pau Espin Pedrol ] | ||||||
|  |   * legacy-mgcp: Add jitter buffer on the uplink receiver | ||||||
|  |   * legacy-mgcp: switch to new osmux output APIs | ||||||
|  |   * mgcp: mgcp_osmux: use conn_bts when forwarding pkts from bsc_nat | ||||||
|  |   * mgcp: switch to new osmux output APIs | ||||||
|  |   * debian: Package installed example doc files | ||||||
|  |   * gitignore: Add m4 scripts from m4 subdir | ||||||
|  |  | ||||||
|  |   [ Neels Hofmeyr ] | ||||||
|  |   * api doc: fix parameter name for mgcp_conn_create() | ||||||
|  |   * mgcp-client: add mgcp_conn_get_ci() | ||||||
|  |   * mgcp_client_fsm: improve error logging | ||||||
|  |   * cosmetic: fix doxygen comment markers | ||||||
|  |   * cosmetic: mgcp_network.c: merge one LOGPC to its preceding LOGP | ||||||
|  |   * IuUP hack: make RTP patching less general | ||||||
|  |  | ||||||
|  |   [ Harald Welte ] | ||||||
|  |   * cosmetic: fix typo in log message: 'abrupt' instead of 'aprupt' | ||||||
|  |  | ||||||
|  |   [ Daniel Willmann ] | ||||||
|  |   * git-version-gen: Don't check for .git directory | ||||||
|  |  | ||||||
|  |  -- Pau Espin Pedrol <pespin@sysmocom.de>  Fri, 27 Jul 2018 19:05:22 +0200 | ||||||
|  |  | ||||||
|  | osmo-mgw (1.3.0) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   [ Pau Espin Pedrol ] | ||||||
|  |   * contrib: Add osmo-mgw systemd service | ||||||
|  |   * legacy: mgcp_protocol: Don't print osmux stats if it is off | ||||||
|  |   * mgcp_stat: Don't print osmux stats if it is off | ||||||
|  |  | ||||||
|  |   [ Neels Hofmeyr ] | ||||||
|  |   * fix segfault: DLCX for unknown endpoint: dont try to log NULL endpoint | ||||||
|  |   * MGCP endpoints: parse as decimal, not hex | ||||||
|  |   * add --enable-sanitize config option | ||||||
|  |   * legacy_mgcp: mgcp_test: sanitize: free msgb_ctx | ||||||
|  |   * mgcp_test: test_packet_error_detection: sanitize: free all conns | ||||||
|  |   * mgcp_test: test_no_cycle: sanitize: free endp | ||||||
|  |   * mgcp_test: sanitize: free msgb_ctx | ||||||
|  |   * mgcp_client: don't configure "bts base" | ||||||
|  |   * Revert "mgcp_client: don't configure "bts base"" until osmo-msc is ready | ||||||
|  |   * mgcp_client: add transaction cleanup | ||||||
|  |   * mgcp_client_test makefile: add update_exp target | ||||||
|  |   * cosmetic: mgcp_network: typo in log | ||||||
|  |   * osmo-mgw: Add talloc context introspection via VTY | ||||||
|  |   * mgcp_client: show failure by MGCP SDP section parsing test | ||||||
|  |   * mgcp_client: cosmetic: clean up SDP params parsing | ||||||
|  |   * mgcp_client: detect SDP section-start parsing errors | ||||||
|  |   * compiler warning: ignore deprecated in mgcp_client_test.c | ||||||
|  |   * configure: add --enable-werror | ||||||
|  |   * jenkins.sh: add --enable-werror to configure flags | ||||||
|  |   * cosmetic: mgcp, legacy_mgcp: drop unused vty.h definitions | ||||||
|  |   * use osmo_init_logging2() with proper talloc ctx | ||||||
|  |  | ||||||
|  |   [ Philipp Maier ] | ||||||
|  |   * osmux: fix nullpointer dereference | ||||||
|  |   * cosmetic: guard dead osmux vty code with ifdef | ||||||
|  |   * cosmetic: remove prefix "net" from rtp related vty commands | ||||||
|  |   * doc: update sample config file | ||||||
|  |   * cosmetic: use correct VTY port number constant | ||||||
|  |   * vty: simplify endpoint allocation | ||||||
|  |   * vty: do not change number_endpoints at runtime | ||||||
|  |   * MGCP: Connection Identifiers are hex strings | ||||||
|  |   * libosmo-mgcp: Connection Identifiers are allocated by MGW, not CA | ||||||
|  |   * client: use osmo_strlcpy instead of strncpy | ||||||
|  |   * cosmetic: fix sourcecode formatting | ||||||
|  |   * cosmetic: clearly mark endpoint numbers as hex | ||||||
|  |   * client: use string as connection identifier | ||||||
|  |   * conn: remove assertions | ||||||
|  |   * mgcp_test: fix wrong strcmp() parameters | ||||||
|  |   * mgcp_test: fix nullpointer dereference | ||||||
|  |   * mgcp_test: add returncode check | ||||||
|  |   * mgcp_test: fix possible double free | ||||||
|  |   * mcgp_client: mgcp_msg_gen(): add checks to verify params | ||||||
|  |   * network: use originating RTP packet address for loopback | ||||||
|  |   * client: mgcp_response_parse_params: check rtp port | ||||||
|  |   * mgcp: allow endpoints beginning from zero | ||||||
|  |   * client/common: move constant MGCP_ENDPOINT_MAXLEN | ||||||
|  |   * mgcp: make domain name configurable | ||||||
|  |   * cosmetic: protocol: remove unnecessary nul termination | ||||||
|  |   * client: do not insist on \n\n when parsing MGCP messages | ||||||
|  |   * main: display mgcp ip/port | ||||||
|  |   * client: make callid in MDCX mandatory | ||||||
|  |   * client: add missing mandatory SDP fields | ||||||
|  |   * mgcp: permit wildcarded endpoint assignment (CRCX) | ||||||
|  |   * mgcp: add prefix to virtual trunk | ||||||
|  |   * client: eliminate destructive parameter parsing | ||||||
|  |   * client: eliminate destructive head parsing | ||||||
|  |   * cosmetic: client: add doxygen comments | ||||||
|  |   * protocol: fix problem with line break and OSMUX | ||||||
|  |   * protocol: fix missing carriage return | ||||||
|  |   * client: fix sdp parameter ordering | ||||||
|  |   * protocol: check the packetization in local cx options | ||||||
|  |   * cosmetic: remove spaces from pointer symbol | ||||||
|  |   * client: Do not accept endpoint ids with wildcards in responses | ||||||
|  |   * client: do not accept endpoint ids without @ character in responses | ||||||
|  |   * client: prohibit endpoint ids without @ character | ||||||
|  |   * protocol: on wildcarded CRCX return endpoint number as hex | ||||||
|  |   * msg: fix response code on exhausted endp resources | ||||||
|  |   * cosmetic: move mgcp_release_endp() to mgcp_ep.c | ||||||
|  |   * client: use heap to store mgcp_response | ||||||
|  |   * ep: move endpoint struct and define to mgcp_ep.h | ||||||
|  |   * cosmetic: rename mgcp_release_endp to mgcp_endp_release | ||||||
|  |   * cosmetic: rename mgcp_ep.c/h to mgcp_endp.c/h | ||||||
|  |   * protocol: reject DLCX/CRCX/MDCX on unsupported parameters | ||||||
|  |   * protocol: exit cleanly when local cx options check fails | ||||||
|  |   * cosmetic: Add missing \n on log line | ||||||
|  |   * protocol: check requested connection mode | ||||||
|  |   * protocol: fix tagging of wildcarded requests | ||||||
|  |   * protocol: prohibit wildcarded requests for MDCX and DLCX | ||||||
|  |   * mgcp: fix use-after-free and add callback for endpoint cleanup | ||||||
|  |   * client: add an optional FSM interface | ||||||
|  |   * mgcp_client_fsm: Add FSM event names | ||||||
|  |   * cosmetic: mgcp_client_fsm: rename enums | ||||||
|  |   * cosmetic: rename function .._conn_reset() to .._conn_init() | ||||||
|  |   * mgcp_conn: do not touch u.rtp in mgcp_conn_alloc() | ||||||
|  |   * cosmetic: rename .._codec_reset() to .._codec_init() | ||||||
|  |   * mgcp_conn: add function mgcp_rtp_conn_cleanup() | ||||||
|  |   * stats: use libosmocore rate counter for in/out_stream.err_ts_counter | ||||||
|  |  | ||||||
|  |   [ Alexander Couzens ] | ||||||
|  |   * debian/control: correct library dependency of osmo-mgw against libosmo-mgcp1 | ||||||
|  |   * debian: include systemd service osmo-mgw.service | ||||||
|  |   * Revert "stats: use libosmocore rate counter for in/out_stream.err_ts_counter" | ||||||
|  |  | ||||||
|  |   [ Harald Welte ] | ||||||
|  |   * cosmetic: fix whitespaces; we use tabs for indentation | ||||||
|  |   * Fix possible buffer overflow in mgcp_conn_dump() | ||||||
|  |   * osmo-mgw: Update copyright statement | ||||||
|  |   * osmo-mgw: Config file is osmo-mgw.cfg, and not mgcp.cfg | ||||||
|  |   * osmo-mgw: Fix copyright notice | ||||||
|  |   * strct mgcp_rtp_state: Group + document struct members related to patching | ||||||
|  |   * mgcp_rtp_state: grup 'stats' members into sub-structure | ||||||
|  |   * mgcp_rtp_end: Group statistics members into 'stats' sub-struct | ||||||
|  |   * libosmo-mgcp: Cosmetic spelling fixes in comments | ||||||
|  |   * mgcp_msg: We must parse endpoint numbers as hex, not decimal! | ||||||
|  |   * mgcp_internal.h: document more struct members with comments | ||||||
|  |   * centralize handling of common errors like "endpoint not found" | ||||||
|  |   * Return proper MGCP Error codes, as per spec | ||||||
|  |   * osmo-mgw: Use libosmocore socket abstraction | ||||||
|  |   * osmo-bsc_mgcp: Add LIBOSMONETIF_{CFLAGS,LIBS} | ||||||
|  |   * libosmo-mgcp-client is GPLv2+, not AGPLv3+ | ||||||
|  |   * Turn libosmo-mgcp into local, non-installed library | ||||||
|  |  | ||||||
|  |   [ Stefan Sperling ] | ||||||
|  |   * enable osmo_fsm vty commands in libosmo-mgcp-client vty | ||||||
|  |  | ||||||
|  |  -- Pau Espin Pedrol <pespin@sysmocom.de>  Thu, 03 May 2018 17:40:35 +0200 | ||||||
|  |  | ||||||
|  | osmo-mgw (1.2.0) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   [ Neels Hofmeyr ] | ||||||
|  |   * jenkins: use osmo-clean-workspace.sh before and after build | ||||||
|  |   * vty: skip installing cmds now always installed by default | ||||||
|  |   * mgcp-client vty: use name 'mgw' instead of 'mgcpgw' | ||||||
|  |   * mgcp client: vty: tweak doc strings | ||||||
|  |  | ||||||
|  |   [ Philipp Maier ] | ||||||
|  |   * sdp: refactoring sdp parser/generator | ||||||
|  |   * cosmetic: rename bts_codec to codec_str | ||||||
|  |   * cosmetic: fix coding style for mgcp_parse_sdp_data() | ||||||
|  |   * cosmetic: fix commenting style | ||||||
|  |   * cosmetic: correct whitespaces | ||||||
|  |   * client: fix stderror logging in unit-test | ||||||
|  |   * client: add unified function to generate MGCP messages | ||||||
|  |   * client: add ip address parsing to the client | ||||||
|  |   * protocol: allow wildcarded DLCX | ||||||
|  |   * mgcp: remove port/timeslot calculator functions from  mgcp.h | ||||||
|  |   * network: add separate log category | ||||||
|  |   * cosmetic: make dummy packet handling more explicit | ||||||
|  |   * network: autdetect rtp bind ip-address | ||||||
|  |   * network: fix rtp packet length | ||||||
|  |   * network: remove unused return code | ||||||
|  |  | ||||||
|  |   [ Pau Espin Pedrol ] | ||||||
|  |   * mgcp_client_vty.c: Fix VTY compatibility with 'mgcpgw bts-base' | ||||||
|  |  | ||||||
|  |  -- Harald Welte <laforge@gnumonks.org>  Fri, 10 Nov 2017 11:10:23 +0900 | ||||||
|  |  | ||||||
|  | osmo-mgw (1.1.0) unstable; urgency=medium | ||||||
|  |  | ||||||
|  |   * New upstream release | ||||||
|  |  | ||||||
|  |  -- Harald Welte <lafore@gnumonks.org>  Sat, 28 Oct 2017 12:48:41 +0200 | ||||||
|  |  | ||||||
| osmo-mgw (1.0.2) unstable; urgency=low | osmo-mgw (1.0.2) unstable; urgency=low | ||||||
|  |  | ||||||
|   * First release after major rename. |   * First release after major rename. | ||||||
|   | |||||||
							
								
								
									
										38
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										38
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -1,13 +1,15 @@ | |||||||
| Source: osmo-mgw | Source: osmo-mgw | ||||||
| Section: net | Section: net | ||||||
| Priority: extra | Priority: extra | ||||||
| Maintainer: Alexander Couzens <lynxis@fe80.eu> | Maintainer: Osmocom team <openbsc@lists.osmocom.org> | ||||||
| Build-Depends: debhelper (>=9), | Build-Depends: debhelper (>=9), | ||||||
|                dh-autoreconf, |                dh-autoreconf, | ||||||
|                pkg-config, |                pkg-config, | ||||||
|                autotools-dev, |                autotools-dev, | ||||||
|                libosmocore-dev, |                libosmocore-dev (>= 1.6.0), | ||||||
|                libosmo-netif-dev |                libosmo-netif-dev (>= 1.1.0), | ||||||
|  |                libosmo-abis-dev (>= 1.2.0), | ||||||
|  |                osmo-gsm-manuals-dev (>= 1.2.0) | ||||||
| Standards-Version: 3.9.8 | Standards-Version: 3.9.8 | ||||||
| Vcs-Git: git://git.osmocom.org/osmo-mgw.git | Vcs-Git: git://git.osmocom.org/osmo-mgw.git | ||||||
| Vcs-Browser: https://git.osmocom.org/osmo-mgw/ | Vcs-Browser: https://git.osmocom.org/osmo-mgw/ | ||||||
| @@ -16,25 +18,10 @@ Homepage: https://osmocom.org/projects/osmo-mgw | |||||||
| Package: osmo-mgw | Package: osmo-mgw | ||||||
| Architecture: any | Architecture: any | ||||||
| Multi-Arch: foreign | Multi-Arch: foreign | ||||||
| Depends: libosmo-legacy-mgcp0, ${misc:Depends}, ${shlibs:Depends} | Depends: ${misc:Depends}, ${shlibs:Depends} | ||||||
| Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks | Description: OsmoMGW: Osmocom's Media Gateway for 2G and 3G circuit-switched mobile networks | ||||||
|  |  | ||||||
| Package: libosmo-legacy-mgcp0 | Package: libosmo-mgcp-client9 | ||||||
| Section: libs |  | ||||||
| Architecture: any |  | ||||||
| Multi-Arch: same |  | ||||||
| Pre-Depends: ${misc:Pre-Depends} |  | ||||||
| Depends: ${misc:Depends}, ${shlibs:Depends} |  | ||||||
| Description: libosmo-legacy-mgcp: Osmocom's Media Gateway server library |  | ||||||
|  |  | ||||||
| Package: libosmo-legacy-mgcp-dev |  | ||||||
| Section: libdevel |  | ||||||
| Architecture: any |  | ||||||
| Multi-Arch: same |  | ||||||
| Depends: libosmo-legacy-mgcp0 (= ${binary:Version}), ${misc:Depends} |  | ||||||
| Description: libosmo-legacy-mgcp: Osmocom's Media Gateway server library |  | ||||||
|  |  | ||||||
| Package: libosmo-mgcp-client1 |  | ||||||
| Section: libs | Section: libs | ||||||
| Architecture: any | Architecture: any | ||||||
| Multi-Arch: same | Multi-Arch: same | ||||||
| @@ -46,5 +33,14 @@ Package: libosmo-mgcp-client-dev | |||||||
| Section: libdevel | Section: libdevel | ||||||
| Architecture: any | Architecture: any | ||||||
| Multi-Arch: same | Multi-Arch: same | ||||||
| Depends: libosmo-mgcp-client1 (= ${binary:Version}), ${misc:Depends} | Depends: libosmo-mgcp-client9 (= ${binary:Version}), ${misc:Depends} | ||||||
| Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities | Description: libosmo-mgcp-client: Osmocom's Media Gateway Control Protocol client utilities | ||||||
|  |  | ||||||
|  | Package: osmo-mgw-doc | ||||||
|  | Architecture: all | ||||||
|  | Section: doc | ||||||
|  | Priority: optional | ||||||
|  | Depends: ${misc:Depends} | ||||||
|  | Description: ${misc:Package} PDF documentation | ||||||
|  |  Various manuals: user manual, VTY reference manual and/or | ||||||
|  |  protocol/interface manuals. | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							| @@ -21,11 +21,12 @@ License:   AGPL-3.0+ | |||||||
|  You should have received a copy of the GNU Affero General Public License |  You should have received a copy of the GNU Affero General Public License | ||||||
|  along with this program.  If not, see <http://www.gnu.org/licenses/>. |  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  | ||||||
| Files:     src/libosmo-legacy-mgcp/g711common.h | Files:     src/libosmo-mgcp-client/* include/osmocom/mgcp_client/* | ||||||
| Copyright: 2000 Abramo Bagnara <abramo@alsa-project.org> | Copyright: 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> | ||||||
|  |            Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c) | ||||||
|  |            2009 by Harald Welte <laforge@gnumonks.org> | ||||||
|  |            2009-2011 by Holger Hans Peter Freyther | ||||||
| License:   GPL-2.0+ | License:   GPL-2.0+ | ||||||
|  Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org> |  | ||||||
|  . |  | ||||||
|  This program is free software; you can redistribute it and/or modify |  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 |  it under the terms of the GNU General Public License as published by | ||||||
|  the Free Software Foundation; either version 2 of the License, or |  the Free Software Foundation; either version 2 of the License, or | ||||||
| @@ -37,13 +38,7 @@ License:   GPL-2.0+ | |||||||
|  GNU General Public License for more details. |  GNU General Public License for more details. | ||||||
|  . |  . | ||||||
|  You should have received a copy of the GNU General Public License |  You should have received a copy of the GNU General Public License | ||||||
|  along with this program; if not, write to the Free Software |  along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA |  | ||||||
|  . |  | ||||||
|  The FSF address in the above text is the old one. |  | ||||||
|  . |  | ||||||
|  On Debian systems, the complete text of the GNU General Public License |  | ||||||
|  Version 2 can be found in `/usr/share/common-licenses/GPL-2'. |  | ||||||
|  |  | ||||||
| Files:     tests/vty_test_runner.py | Files:     tests/vty_test_runner.py | ||||||
| Copyright: 2013 Holger Hans Peter Freyther | Copyright: 2013 Holger Hans Peter Freyther | ||||||
| @@ -68,15 +63,3 @@ License:   GPL-3.0+ | |||||||
| Files:     osmoappdesc.py | Files:     osmoappdesc.py | ||||||
| Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com> | Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com> | ||||||
| License:   GPL-3.0+ | License:   GPL-3.0+ | ||||||
|  |  | ||||||
| Files:     src/libosmo-legacy-mgcp/mgcp_osmux.c |  | ||||||
| Copyright: 2012-2013 On Waves ehf <http://www.on-waves.com> |  | ||||||
|            2012-2013 Pablo Neira Ayuso <pablo@gnumonks.org> |  | ||||||
| License:   AGPL-3.0+ |  | ||||||
|  All rights not specifically granted under this license are reserved. |  | ||||||
|  . |  | ||||||
|  This program is free software; you can redistribute it and/or modify it |  | ||||||
|  under the terms of the GNU Affero General Public License as published by the |  | ||||||
|  Free Software Foundation; either version 3 of the License, or (at your |  | ||||||
|  option) any later version. |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								debian/libosmo-legacy-mgcp-dev.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								debian/libosmo-legacy-mgcp-dev.install
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +0,0 @@ | |||||||
| usr/include/osmocom/legacy_mgcp |  | ||||||
| usr/lib/*/*legacy-mgcp*.so |  | ||||||
| usr/lib/*/*legacy-mgcp*.a |  | ||||||
| usr/lib/*/pkgconfig/*legacy-mgcp*.pc |  | ||||||
							
								
								
									
										1
									
								
								debian/libosmo-legacy-mgcp0.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-legacy-mgcp0.install
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | |||||||
| usr/lib/*/*legacy-mgcp*.so.* |  | ||||||
							
								
								
									
										1
									
								
								debian/libosmo-mgcp-client1.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-mgcp-client1.install
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | |||||||
| usr/lib/*/*mgcp-client*.so.* |  | ||||||
							
								
								
									
										1
									
								
								debian/libosmo-mgcp-client9.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-mgcp-client9.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | usr/lib/*/libosmo-mgcp-client.so.* | ||||||
							
								
								
									
										1
									
								
								debian/osmo-mgw-doc.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-mgw-doc.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | usr/share/doc/osmo-mgw-doc/*.pdf | ||||||
							
								
								
									
										6
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,5 @@ | |||||||
| usr/bin | etc/osmocom/osmo-mgw.cfg | ||||||
|  | lib/systemd/system/osmo-mgw.service | ||||||
|  | usr/bin/osmo-mgw | ||||||
|  | usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw.cfg | ||||||
|  | usr/share/doc/osmo-mgw/examples/osmo-mgw/osmo-mgw-abis_e1.cfg | ||||||
|   | |||||||
							
								
								
									
										12
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							| @@ -26,8 +26,14 @@ CFLAGS += -g | |||||||
| #override_dh_install: | #override_dh_install: | ||||||
| #	dh_install --list-missing -X.la -X.pyc -X.pyo | #	dh_install --list-missing -X.la -X.pyc -X.pyo | ||||||
|  |  | ||||||
| override_dh_autoreconf: | override_dh_auto_test: | ||||||
| 	echo $(VERSION) > .tarball-version | 	dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false) | ||||||
| 	dh_autoreconf |  | ||||||
|  | override_dh_auto_configure: | ||||||
|  | 	dh_auto_configure -- --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals | ||||||
|  |  | ||||||
|  | # Don't create .pdf.gz files (barely saves space and they can't be opened directly by most pdf readers) | ||||||
|  | override_dh_compress: | ||||||
|  | 	dh_compress -X.pdf | ||||||
|  |  | ||||||
| # See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg | # See https://www.debian.org/doc/manuals/developers-reference/best-pkging-practices.html#bpp-dbg | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
| SUBDIRS = \ | SUBDIRS = \ | ||||||
| 	examples \ | 	examples \ | ||||||
|  | 	manuals \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|   | |||||||
| @@ -1,3 +1,11 @@ | |||||||
|  | OSMOCONF_FILES = \ | ||||||
|  | 	osmo-mgw/osmo-mgw.cfg | ||||||
|  |  | ||||||
|  | osmoconfdir = $(sysconfdir)/osmocom | ||||||
|  | osmoconf_DATA = $(OSMOCONF_FILES) | ||||||
|  |  | ||||||
|  | EXTRA_DIST = $(OSMOCONF_FILES) | ||||||
|  |  | ||||||
| CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,' | CFG_FILES = find $(srcdir) -name '*.cfg*' | sed -e 's,^$(srcdir),,' | ||||||
|  |  | ||||||
| dist-hook: | dist-hook: | ||||||
|   | |||||||
| @@ -1,14 +0,0 @@ | |||||||
| ! |  | ||||||
| ! MGCP configuration example |  | ||||||
| ! |  | ||||||
| mgcp |  | ||||||
|  !local ip 10.23.24.2 |  | ||||||
|  !bts ip 10.24.24.1 |  | ||||||
|  !bind ip 10.23.24.1 |  | ||||||
|  bind port 2427 |  | ||||||
|  rtp base 4000 |  | ||||||
|  rtp force-ptime 20 |  | ||||||
|  sdp audio payload number 98 |  | ||||||
|  sdp audio payload name AMR/8000 |  | ||||||
|  number endpoints 31 |  | ||||||
|  no rtcp-omit |  | ||||||
							
								
								
									
										25
									
								
								doc/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								doc/examples/osmo-mgw/osmo-mgw-abis_e1.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | ! | ||||||
|  | ! MGCP configuration example | ||||||
|  | ! | ||||||
|  | e1_input | ||||||
|  |  e1_line 0 driver dahdi | ||||||
|  |  e1_line 0 port 0 | ||||||
|  | mgcp | ||||||
|  |   bind ip 127.0.0.1 | ||||||
|  |   rtp port-range 4002 16000 | ||||||
|  |   rtp bind-ip 127.0.0.1 | ||||||
|  |   rtp ip-probing | ||||||
|  |   rtp ip-dscp 46 | ||||||
|  |   bind port 2427 | ||||||
|  |   sdp audio payload number 98 | ||||||
|  |   sdp audio payload name GSM | ||||||
|  |   number endpoints 512 | ||||||
|  |   loop 0 | ||||||
|  |   force-realloc 1 | ||||||
|  |   rtcp-omit | ||||||
|  |   rtp-patch ssrc | ||||||
|  |   rtp-patch timestamp | ||||||
|  |   trunk 1 | ||||||
|  |    rtp keep-alive once | ||||||
|  |    no rtp keep-alive | ||||||
|  |    line 0 | ||||||
							
								
								
									
										18
									
								
								doc/examples/osmo-mgw/osmo-mgw.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								doc/examples/osmo-mgw/osmo-mgw.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | ! | ||||||
|  | ! MGCP configuration example | ||||||
|  | ! | ||||||
|  | mgcp | ||||||
|  |   bind ip 127.0.0.1 | ||||||
|  |   rtp port-range 4002 16000 | ||||||
|  |   rtp bind-ip 127.0.0.1 | ||||||
|  |   rtp ip-probing | ||||||
|  |   rtp ip-dscp 46 | ||||||
|  |   bind port 2427 | ||||||
|  |   sdp audio payload number 98 | ||||||
|  |   sdp audio payload name GSM | ||||||
|  |   number endpoints 512 | ||||||
|  |   loop 0 | ||||||
|  |   force-realloc 1 | ||||||
|  |   rtcp-omit | ||||||
|  |   rtp-patch ssrc | ||||||
|  |   rtp-patch timestamp | ||||||
							
								
								
									
										23
									
								
								doc/manuals/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								doc/manuals/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | EXTRA_DIST = osmomgw-usermanual.adoc \ | ||||||
|  |     osmomgw-usermanual-docinfo.xml \ | ||||||
|  |     osmomgw-vty-reference.xml \ | ||||||
|  |     regen_doc.sh \ | ||||||
|  |     chapters \ | ||||||
|  |     vty | ||||||
|  |  | ||||||
|  | if BUILD_MANUALS | ||||||
|  |   ASCIIDOC = osmomgw-usermanual.adoc | ||||||
|  |   ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc | ||||||
|  |   include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc | ||||||
|  |  | ||||||
|  |   VTY_REFERENCE = osmomgw-vty-reference.xml | ||||||
|  |   include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc | ||||||
|  |  | ||||||
|  |   BUILT_REFERENCE_XML = $(builddir)/vty/mgw_vty_reference.xml | ||||||
|  |   $(builddir)/vty/mgw_vty_reference.xml: $(top_builddir)/src/osmo-mgw/osmo-mgw | ||||||
|  | 	mkdir -p $(builddir)/vty | ||||||
|  | 	$(top_builddir)/src/osmo-mgw/osmo-mgw --vty-ref-xml > $@ | ||||||
|  |  | ||||||
|  |   OSMO_REPOSITORY = osmo-mgw | ||||||
|  |   include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc | ||||||
|  | endif | ||||||
							
								
								
									
										129
									
								
								doc/manuals/chapters/configuration.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								doc/manuals/chapters/configuration.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | |||||||
|  | == Configuring OsmoMGW | ||||||
|  |  | ||||||
|  | A basic configation of OsmoMGW mainly consists of specifying the IP address | ||||||
|  | and port on which to listen to MGCP commands, but changing the port range at | ||||||
|  | which the RTP streams terminate as well as limiting operation to a single call | ||||||
|  | agent can be done as well as changing the number of endpoints. | ||||||
|  |  | ||||||
|  | === Configuring MGCP | ||||||
|  |  | ||||||
|  | By default OsmoMGW listens for MGCP on port 2427 on any IP address. If a call | ||||||
|  | agent address is configured then OsmoMGW will only answer MGCP commands from | ||||||
|  | that IP port 2727, otherwise all sources are handled. A domain can be | ||||||
|  | specified | ||||||
|  |  | ||||||
|  | .Example: MGCP configuration | ||||||
|  | ---- | ||||||
|  | OsmoMGW(config-mgcp)# bind ip 127.0.0.1 | ||||||
|  | OsmoMGW(config-mgcp)# bind port 2427 | ||||||
|  | OsmoMGW(config-mgcp)# call-agent ip 127.0.0.1 | ||||||
|  | OsmoMGW(config-mgcp)# domain mgw-bsc | ||||||
|  | OsmoMGW(config-mgcp)# local ip 127.0.0.1 | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | === Configuring the trunk | ||||||
|  |  | ||||||
|  | The first trunk is considered a virtual trunk in OsmoMGW. All | ||||||
|  | endpoints of type "rtpbridge" are routed here. The virtual trunk is configured | ||||||
|  | in the config-mgcp context. | ||||||
|  |  | ||||||
|  | All other trunks are configured in the config-mgcp-trunk context, but the | ||||||
|  | commands used are identical. Right now trunks are considered only for "ds/e1" | ||||||
|  | type endpoints. | ||||||
|  |  | ||||||
|  | .Example: MGCP trunk configuration | ||||||
|  | ---- | ||||||
|  | OsmoMGW(config-mgcp)# number endpoints 63 <1> | ||||||
|  | OsmoMGW(config-mgcp)# rtp bind-ip 10.0.0.1 <2> | ||||||
|  | OsmoMGW(config-mgcp)# rtp port-range 12000-14000 <3> | ||||||
|  | ---- | ||||||
|  | <1> Maximum number of endpoints that can be allocated at once | ||||||
|  | <2> Use this IP when binding RTP endpoints | ||||||
|  | <3> Use ports in this range when binding RTP endpoints | ||||||
|  |  | ||||||
|  | There are some options to tweak how RTP forwarding behaves in OsmoMGW: | ||||||
|  |  | ||||||
|  | .Example: MGCP trunk rtp options | ||||||
|  | ---- | ||||||
|  | OsmoMGW(config-mgcp)# rtp keep-alive 30 <1> | ||||||
|  | OsmoMGW(config-mgcp)# rtp-patch ssrc <2> | ||||||
|  | OsmoMGW(config-mgcp)# rtp-patch timestamp <3> | ||||||
|  | ---- | ||||||
|  | <1> Send dummy UDP packets periodically to RTP destination | ||||||
|  | <2> Hide SSRC changes | ||||||
|  | <3> Ensure RTP timestamp is aligned with frame duration | ||||||
|  |  | ||||||
|  | NOTE: Changes to trunks that affect resource allocation, such as newly created | ||||||
|  | trunks or a change of the number of available endpoints, require a full restart | ||||||
|  | of osmo-mgw! | ||||||
|  |  | ||||||
|  | === E1 trunk considerations | ||||||
|  |  | ||||||
|  | While the RTP bridge trunks are natively based on IP no special considerations | ||||||
|  | are required during setup. E1 trunks are mapped on a physical E1 line, which has | ||||||
|  | to be configured as shown below. | ||||||
|  |  | ||||||
|  | .Example: E1 line setup | ||||||
|  | ---- | ||||||
|  | OsmoMGW(config-e1_input)# e1_line 0 driver dahdi <1> | ||||||
|  | OsmoMGW(config-e1_input)# e1_line 0 port 2 <2> | ||||||
|  | ---- | ||||||
|  | <1> Name of the libosmo-abis driver implementation ("dahdi") | ||||||
|  | <2> Port number of the physical E1 port to use (2) | ||||||
|  |  | ||||||
|  | In osmo-mgw the e1_input node is used to configure the physical E1 line. The | ||||||
|  | line number will be used internally to identify the configured E1 line. The | ||||||
|  | port number is the physical E1 connector (sometimes called 'span') at the E1 | ||||||
|  | hardware. Per trunk an individual E1 line will be needed. Beware that the E1 | ||||||
|  | driver may also need configuration settings that are not discussed here. | ||||||
|  |  | ||||||
|  | .Example: E1 trunk setup | ||||||
|  | ---- | ||||||
|  | OsmoMGW(config-mgcp)# trunk 0 <1> | ||||||
|  | OsmoMGW(config-mgcp-trunk)# line 0 <2> | ||||||
|  | ---- | ||||||
|  | <1> Creation of a trunk (0) | ||||||
|  | <2> Reference to the E1 line to use (0) | ||||||
|  |  | ||||||
|  | The E1 trunk is created along with a number, typically starting at 0, but if | ||||||
|  | required any number from 0-64 is allowed. The E1 trunk configuration concerning | ||||||
|  | the IP related aspects is nearly identical to the configuration of the virtual | ||||||
|  | trunk. However, it is important that the user assigns one of the E1 line numbers | ||||||
|  | that were configured under the e1_input node. | ||||||
|  |  | ||||||
|  | NOTE: The endpoint name that is used on MGCP level will include the trunk number, | ||||||
|  | not the E1 line number. For simplicity (and compatibility with OsmoBSC) it is | ||||||
|  | recommended to use equal numbers for trunk and E1 line. However, if required any | ||||||
|  | E1 line can be mapped flexible on any trunk as long as the mapping is bijective. | ||||||
|  |  | ||||||
|  | .Example: A typical configuration with one E1 trunk | ||||||
|  | ---- | ||||||
|  | e1_input | ||||||
|  |  e1_line 0 driver dahdi | ||||||
|  |  e1_line 0 port 2 | ||||||
|  | mgcp | ||||||
|  |  bind ip 127.0.0.1 | ||||||
|  |  rtp net-range 6000 6011 | ||||||
|  |  rtp net-bind-ip 192.168.100.130 | ||||||
|  |  rtp ip-probing | ||||||
|  |  rtp ip-dscp 46 | ||||||
|  |  no rtp keep-alive | ||||||
|  |  bind port 2428 | ||||||
|  |  number endpoints 30 | ||||||
|  |  loop 0 | ||||||
|  |  force-realloc 1 | ||||||
|  |  osmux off | ||||||
|  |  rtp-patch rfc5993hr | ||||||
|  |  trunk 0 | ||||||
|  |   rtp keep-alive once | ||||||
|  |   no rtp keep-alive | ||||||
|  |   line 0 | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | NOTE: One E1 trunk always covers a whole E1 line. All subslots (I.640) will be mapped | ||||||
|  | to individual MGCP endpoints. As long as the endpoints remain unused the | ||||||
|  | underlying E1 timeslot is not used. | ||||||
|  |  | ||||||
|  | NOTE: The E1 trunk implementation also works with T1 lines, however since T1 has | ||||||
|  | 24 instead of 31 usable timeslots only the endpoints that fall into that 1-24 timeslot | ||||||
|  | range will be useable. | ||||||
							
								
								
									
										4
									
								
								doc/manuals/chapters/counters.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								doc/manuals/chapters/counters.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | [[counters]] | ||||||
|  | == Counters | ||||||
|  |  | ||||||
|  | include::./counters_generated.adoc[] | ||||||
							
								
								
									
										83
									
								
								doc/manuals/chapters/counters_generated.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								doc/manuals/chapters/counters_generated.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  |  | ||||||
|  | // autogenerated by show asciidoc counters | ||||||
|  | These counters and their description based on OsmoMGW 1.5.0.64-189f (OsmoMGW). | ||||||
|  |  | ||||||
|  | === Rate Counters | ||||||
|  |  | ||||||
|  | // generating tables for rate_ctr_group | ||||||
|  | // rate_ctr_group table aggregated statistics for all rtp connections | ||||||
|  | .all_rtp_conn - aggregated statistics for all rtp connections | ||||||
|  | [options="header"] | ||||||
|  | |=== | ||||||
|  | | Name | Reference | Description | ||||||
|  | | all_rtp:err_tstmp_in | <<all_rtp_conn_all_rtp:err_tstmp_in>> | Total inbound rtp-stream timestamp errors. | ||||||
|  | | all_rtp:err_tstmp_out | <<all_rtp_conn_all_rtp:err_tstmp_out>> | Total outbound rtp-stream timestamp errors. | ||||||
|  | | all_rtp:packets_rx | <<all_rtp_conn_all_rtp:packets_rx>> | Total inbound rtp packets. | ||||||
|  | | all_rtp:octets_rx | <<all_rtp_conn_all_rtp:octets_rx>> | Total inbound rtp octets. | ||||||
|  | | all_rtp:packets_tx | <<all_rtp_conn_all_rtp:packets_tx>> | Total outbound rtp packets. | ||||||
|  | | all_rtp:octets_tx | <<all_rtp_conn_all_rtp:octets_tx>> | Total outbound rtp octets. | ||||||
|  | | all_rtp:dropped | <<all_rtp_conn_all_rtp:dropped>> | Total dropped rtp packets. | ||||||
|  | | all_rtp:num_closed_conns | <<all_rtp_conn_all_rtp:num_closed_conns>> | Total number of rtp connections closed. | ||||||
|  | |=== | ||||||
|  | // rate_ctr_group table dlcx statistics | ||||||
|  | .dlcx - dlcx statistics | ||||||
|  | [options="header"] | ||||||
|  | |=== | ||||||
|  | | Name | Reference | Description | ||||||
|  | | dlcx:success | <<dlcx_dlcx:success>> | DLCX command processed successfully. | ||||||
|  | | dlcx:wildcard | <<dlcx_dlcx:wildcard>> | wildcard names in DLCX commands are unsupported. | ||||||
|  | | dlcx:no_conn | <<dlcx_dlcx:no_conn>> | endpoint specified in DLCX command has no active connections. | ||||||
|  | | dlcx:callid | <<dlcx_dlcx:callid>> | CallId specified in DLCX command mismatches endpoint's CallId . | ||||||
|  | | dlcx:connid | <<dlcx_dlcx:connid>> | connection ID specified in DLCX command does not exist on endpoint. | ||||||
|  | | dlcx:unhandled_param | <<dlcx_dlcx:unhandled_param>> | unhandled parameter in DLCX command. | ||||||
|  | | dlcx:rejected | <<dlcx_dlcx:rejected>> | connection deletion rejected by policy. | ||||||
|  | | dlcx:deferred | <<dlcx_dlcx:deferred>> | connection deletion deferred by policy. | ||||||
|  | |=== | ||||||
|  | // rate_ctr_group table mdcx statistics | ||||||
|  | .mdcx - mdcx statistics | ||||||
|  | [options="header"] | ||||||
|  | |=== | ||||||
|  | | Name | Reference | Description | ||||||
|  | | mdcx:success | <<mdcx_mdcx:success>> | MDCX command processed successfully. | ||||||
|  | | mdcx:wildcard | <<mdcx_mdcx:wildcard>> | wildcard endpoint names in MDCX commands are unsupported. | ||||||
|  | | mdcx:no_conn | <<mdcx_mdcx:no_conn>> | endpoint specified in MDCX command has no active connections. | ||||||
|  | | mdcx:callid | <<mdcx_mdcx:callid>> | invalid CallId specified in MDCX command. | ||||||
|  | | mdcx:connid | <<mdcx_mdcx:connid>> | invalid connection ID specified in MDCX command. | ||||||
|  | | crcx:unhandled_param | <<mdcx_crcx:unhandled_param>> | unhandled parameter in MDCX command. | ||||||
|  | | mdcx:no_connid | <<mdcx_mdcx:no_connid>> | no connection ID specified in MDCX command. | ||||||
|  | | mdcx:conn_not_found | <<mdcx_mdcx:conn_not_found>> | connection specified in MDCX command does not exist. | ||||||
|  | | mdcx:invalid_mode | <<mdcx_mdcx:invalid_mode>> | invalid connection mode in MDCX command. | ||||||
|  | | mdcx:conn_opt | <<mdcx_mdcx:conn_opt>> | connection options invalid. | ||||||
|  | | mdcx:no_remote_conn_desc | <<mdcx_mdcx:no_remote_conn_desc>> | no opposite end specified for connection. | ||||||
|  | | mdcx:start_rtp_failure | <<mdcx_mdcx:start_rtp_failure>> | failure to start RTP processing. | ||||||
|  | | mdcx:conn_rejected | <<mdcx_mdcx:conn_rejected>> | connection rejected by policy. | ||||||
|  | | mdcx:conn_deferred | <<mdcx_mdcx:conn_deferred>> | connection deferred by policy. | ||||||
|  | |=== | ||||||
|  | // rate_ctr_group table crxc statistics | ||||||
|  | .crcx - crxc statistics | ||||||
|  | [options="header"] | ||||||
|  | |=== | ||||||
|  | | Name | Reference | Description | ||||||
|  | | crcx:success | <<crcx_crcx:success>> | CRCX command processed successfully. | ||||||
|  | | crcx:bad_action | <<crcx_crcx:bad_action>> | bad action in CRCX command. | ||||||
|  | | crcx:unhandled_param | <<crcx_crcx:unhandled_param>> | unhandled parameter in CRCX command. | ||||||
|  | | crcx:missing_callid | <<crcx_crcx:missing_callid>> | missing CallId in CRCX command. | ||||||
|  | | crcx:invalid_mode | <<crcx_crcx:invalid_mode>> | invalid connection mode in CRCX command. | ||||||
|  | | crcx:limit_exceeded | <<crcx_crcx:limit_exceeded>> | limit of concurrent connections was reached. | ||||||
|  | | crcx:unkown_callid | <<crcx_crcx:unkown_callid>> | unknown CallId in CRCX command. | ||||||
|  | | crcx:alloc_conn_fail | <<crcx_crcx:alloc_conn_fail>> | connection allocation failure. | ||||||
|  | | crcx:no_remote_conn_desc | <<crcx_crcx:no_remote_conn_desc>> | no opposite end specified for connection. | ||||||
|  | | crcx:start_rtp_failure | <<crcx_crcx:start_rtp_failure>> | failure to start RTP processing. | ||||||
|  | | crcx:conn_rejected | <<crcx_crcx:conn_rejected>> | connection rejected by policy. | ||||||
|  | | crcx:no_osmux | <<crcx_crcx:no_osmux>> | no osmux offered by peer. | ||||||
|  | | crcx:conn_opt | <<crcx_crcx:conn_opt>> | connection options invalid. | ||||||
|  | | crcx:codec_nego | <<crcx_crcx:codec_nego>> | codec negotiation failure. | ||||||
|  | | crcx:bind_port | <<crcx_crcx:bind_port>> | port bind failure. | ||||||
|  | |=== | ||||||
|  | == Osmo Stat Items | ||||||
|  |  | ||||||
|  | // generating tables for osmo_stat_items | ||||||
|  | == Osmo Counters | ||||||
|  |  | ||||||
|  | // generating tables for osmo_counters | ||||||
|  | // there are no ungrouped osmo_counters | ||||||
							
								
								
									
										94
									
								
								doc/manuals/chapters/mgcp_endpoints.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								doc/manuals/chapters/mgcp_endpoints.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | |||||||
|  | == MGCP Endpoints | ||||||
|  |  | ||||||
|  | MGCP organizes the switching resources in so called endpoints. Each endpoint is | ||||||
|  | referenced by its unique identifier. While RFC 3435 specifies a naming scheme, the | ||||||
|  | actual identifier naming is subject to the implementation and configuration. | ||||||
|  |  | ||||||
|  | === RTP proxy / RTP bridge endpoints | ||||||
|  |  | ||||||
|  | OsmoMGW implements a freely configurable number of `rtpbridge` endpoints. Those | ||||||
|  | endpoints are able to host two connections at a time to model the functionality | ||||||
|  | of a tandem switch. | ||||||
|  |  | ||||||
|  | RTP bridge endpoint identifiers are referenced by the string `rtpbridge/`, a | ||||||
|  | hexadecimal number without leading zeros and a domain name (configurable). | ||||||
|  |  | ||||||
|  | ---- | ||||||
|  | rtpbridge/<number>@<domain> | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | .Example: List of virtual endpoints | ||||||
|  | ---- | ||||||
|  | rtpbridge/1@mgw | ||||||
|  | rtpbridge/2@mgw | ||||||
|  | rtpbridge/3@mgw | ||||||
|  | rtpbridge/4@mgw | ||||||
|  | rtpbridge/5@mgw | ||||||
|  | rtpbridge/6@mgw | ||||||
|  | rtpbridge/7@mgw | ||||||
|  | rtpbridge/8@mgw | ||||||
|  | rtpbridge/9@mgw | ||||||
|  | rtpbridge/a@mgw | ||||||
|  | rtpbridge/b@mgw | ||||||
|  | rtpbridge/c@mgw | ||||||
|  | rtpbridge/d@mgw | ||||||
|  | rtpbridge/e@mgw | ||||||
|  | rtpbridge/f@mgw | ||||||
|  | rtpbridge/10@mgw | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | === E1/T1 endpoints | ||||||
|  |  | ||||||
|  | OsmoMGW supports E1 subslot multiplexing as specified by I.460. All possible | ||||||
|  | subslot combinations are mapped on individual endpoints. The endpoint names | ||||||
|  | are prefixed with `ds/e1-` followed by the trunk number and the E1 timeslot. | ||||||
|  | The subslot is defined by a bit rate and a bit offset. | ||||||
|  |  | ||||||
|  | ---- | ||||||
|  | ds/e1-<trunk>/s-<timeslot>/su<bitrate>-<bitoffset>@<domain> | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | .Example: List of endpoints on E1 trunk 0 at E1 timeslot 2 | ||||||
|  | ---- | ||||||
|  | ds/e1-0/s-2/su64-0@mgw | ||||||
|  | ds/e1-0/s-2/su32-0@mgw | ||||||
|  | ds/e1-0/s-2/su32-4@mgw | ||||||
|  | ds/e1-0/s-2/su16-0@mgw | ||||||
|  | ds/e1-0/s-2/su16-2@mgw | ||||||
|  | ds/e1-0/s-2/su16-4@mgw | ||||||
|  | ds/e1-0/s-2/su16-6@mgw | ||||||
|  | ds/e1-0/s-2/su8-0@mgw | ||||||
|  | ds/e1-0/s-2/su8-1@mgw | ||||||
|  | ds/e1-0/s-2/su8-2@mgw | ||||||
|  | ds/e1-0/s-2/su8-3@mgw | ||||||
|  | ds/e1-0/s-2/su8-4@mgw | ||||||
|  | ds/e1-0/s-2/su8-5@mgw | ||||||
|  | ds/e1-0/s-2/su8-6@mgw | ||||||
|  | ds/e1-0/s-2/su8-7@mgw | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | When creating connections on endpoints that reside in one E1 timeslot the call | ||||||
|  | agent must make sure that no overlapping endpoints are used. It is for example | ||||||
|  | not possible to use `ds/e1-0/s-2/su16-2@mgw` and `ds/e1-0/s-2/su8-3@mgw` at the | ||||||
|  | same time because they overlap. | ||||||
|  |  | ||||||
|  | .Subslot overlapping | ||||||
|  | [options="header"] | ||||||
|  | |=== | ||||||
|  | | Bit offset  4+| Subslots | ||||||
|  | | 0 | 8k .2+| 16k .4+| 32k .8+| 64k | ||||||
|  | | 1 | 8k | ||||||
|  | | 2 | 8k .2+| 16k | ||||||
|  | | 3 | 8k | ||||||
|  | | 4 | 8k .2+| 16k .4+| 32k | ||||||
|  | | 5 | 8k | ||||||
|  | | 6 | 8k .2+| 16k | ||||||
|  | | 7 | 8k | ||||||
|  | |=== | ||||||
|  |  | ||||||
|  | NOTE: The current implementation (December 2020) only implements TRAU frame | ||||||
|  | encoding/decoding for 16K and 8K subslots. Endpoints with other bitrates are | ||||||
|  | not yet useable. | ||||||
|  |  | ||||||
|  | NOTE: the VTY command "show mgcp" can be used to get a list of all available | ||||||
|  | endpoints (including identifiers) | ||||||
							
								
								
									
										72
									
								
								doc/manuals/chapters/mgcp_extensions.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								doc/manuals/chapters/mgcp_extensions.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | == MGCP Extensions | ||||||
|  |  | ||||||
|  | The MGCP protocol is extendable. The following non-standard extensions are | ||||||
|  | understood by OsmoMGW. | ||||||
|  |  | ||||||
|  | === `X-Osmo-IGN` | ||||||
|  |  | ||||||
|  | `X-Osmo-IGN` indicates to OsmoMGW that specific items of an endpoint should be | ||||||
|  | ignored, so that it is lenient on mismatching values that would normally | ||||||
|  | indicate collisions or configuration errors. | ||||||
|  |  | ||||||
|  | ==== `X-Osmo-IGN` Format | ||||||
|  |  | ||||||
|  | The value part of `X-Osmo-IGN` must be one or more items separated by one or more | ||||||
|  | spaces. Each item consists of one or more non-whitespace characters. | ||||||
|  |  | ||||||
|  | .Example: `X-Osmo-IGN` format with three ficticious items "X", "abc" and "123". | ||||||
|  | ---- | ||||||
|  | X-Osmo-IGN: X abc 123 | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | `X-Osmo-IGN` must be issued in the MGCP header section (typically as its last item), | ||||||
|  | before the SDP section starts. | ||||||
|  |  | ||||||
|  | ==== Supported `X-Osmo-IGN` Items | ||||||
|  |  | ||||||
|  | Currently, the following `X-Osmo-IGN` items are supported: | ||||||
|  |  | ||||||
|  | * `C`: ignore CallID mismatches, i.e. differing "C" values between connections | ||||||
|  |   on the same endpoint. | ||||||
|  |  | ||||||
|  | .Note: | ||||||
|  | `X-Osmo-IGN` does not support ignoring mismatches on the domain part of | ||||||
|  | an endpoint name, e.g. ignoring a mismatch on "example.com" in | ||||||
|  | `rtpbridge/123abc@example.com`. Instead, you may globally configure OsmoMGW | ||||||
|  | with `mgcp` / `domain *` to permit all domain parts. | ||||||
|  |  | ||||||
|  | ===== `X-Osmo-IGN: C` | ||||||
|  |  | ||||||
|  | By default, OsmoMGW verifies that all CallIDs ("C" values) match for all | ||||||
|  | connections on any one given endpoint. To ignore CallID mismatches, pass a `C` | ||||||
|  | in the `X-Osmo-IGN` header, for the first or the second `CRCX` on an endpoint. | ||||||
|  | When the `X-Osmo-IGN: C` is sent for any one `CRCX` on an endpoint, CallID | ||||||
|  | mismatches will be ignored for that and all subsequent messages for that | ||||||
|  | endpoint. Hence this header only needs to be included once per endpoint, in any | ||||||
|  | `CRCX` message that precedes or coincides with a CallID mismatch. | ||||||
|  |  | ||||||
|  | This is particularly useful for a BSC that is connected to an A/SCCPlite MSC, | ||||||
|  | where the BSC and MSC each are expected to configure their respective own | ||||||
|  | connection on a shared endpoint. For A/SCCPlite, it is impossible for the BSC | ||||||
|  | to know the CallID that the MSC will use, so CallID mismatches are inevitable. | ||||||
|  | See also OsmoBSC, which will by default pass the `X-Osmo-IGN: C` header for | ||||||
|  | endpoints associated with an A/SCCPlite MSC. | ||||||
|  |  | ||||||
|  | .Example: `CRCX` message that instructs OsmoMGW to ignore CallID mismatches | ||||||
|  | ---- | ||||||
|  | CRCX 2 rtpbridge/123abc@mgw MGCP 1.0 | ||||||
|  | M: recvonly | ||||||
|  | C: 2 | ||||||
|  | L: p:20 | ||||||
|  | X-Osmo-IGN: C | ||||||
|  |  | ||||||
|  | v=0 | ||||||
|  | c=IN IP4 123.12.12.123 | ||||||
|  | m=audio 5904 RTP/AVP 97 | ||||||
|  | a=rtpmap:97 GSM-EFR/8000 | ||||||
|  | a=ptime:40 | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | === `X-Osmux` | ||||||
|  |  | ||||||
|  | See <<mgcp-extension-osmux>> | ||||||
							
								
								
									
										115
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | |||||||
|  | [[overview]] | ||||||
|  | == Overview | ||||||
|  |  | ||||||
|  | This manual should help you getting started with OsmoMGW. It will cover | ||||||
|  | aspects of configuring and running the media gateway. | ||||||
|  |  | ||||||
|  | [[intro_overview]] | ||||||
|  | === About OsmoMGW | ||||||
|  |  | ||||||
|  | OsmoMGW is the Osmocom implementation of a media gateway to handle user | ||||||
|  | plane (voice) traffic in cellular networks. It can connect and optionally | ||||||
|  | transcode RTP voice streams between different network elements such as BTSs | ||||||
|  | and external entities like SIP. It is typically co-located with both OsmoBSC | ||||||
|  | and OsmoMSC and controlled by them via MGCP, the Media Gateway Control | ||||||
|  | Protocol. | ||||||
|  |  | ||||||
|  | [[fig-bsc]] | ||||||
|  | .OsmoMGW used with OsmoBSC | ||||||
|  | [graphviz] | ||||||
|  | ---- | ||||||
|  | digraph G { | ||||||
|  |     rankdir = LR; | ||||||
|  |     OsmoBTS -> OsmoBSC [label="Abis/IP"]; | ||||||
|  |     OsmoBSC -> "core-network" [label="3GPP AoIP"]; | ||||||
|  |     OsmoBSC -> OsmoMGW [label="MGCP"]; | ||||||
|  |     OsmoBTS -> OsmoMGW [label="RTP",dir=both]; | ||||||
|  |     OsmoMGW -> "core-network" [label="RTP",dir=both]; | ||||||
|  |     {rank=same OsmoBSC OsmoMGW} | ||||||
|  |     OsmoMGW [color=red]; | ||||||
|  | } | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | [[fig-msc]] | ||||||
|  | .OsmoMGW used with OsmoMSC | ||||||
|  | [graphviz] | ||||||
|  | ---- | ||||||
|  | digraph G { | ||||||
|  |     rankdir = LR; | ||||||
|  |     "2G BSS" -> OsmoMSC [label="3GPP AoIP"]; | ||||||
|  |     OsmoMSC -> OsmoMGW [label="MGCP"]; | ||||||
|  |     "2G BSS" -> OsmoMGW [label="RTP",dir=both]; | ||||||
|  |     OsmoMSC -> OsmoSIP [label="MNCC"]; | ||||||
|  |     OsmoSIP -> PBX [label="SIP Trunk"]; | ||||||
|  |     OsmoMGW -> PBX [label="RTP",dir=both]; | ||||||
|  |     {rank=same OsmoMSC OsmoMGW} | ||||||
|  |     OsmoSIP [label="osmo-sip-connector"]; | ||||||
|  |     OsmoMGW [color=red]; | ||||||
|  |  | ||||||
|  |     hNodeB -> OsmoHNBGW [label="Iuh"]; | ||||||
|  |     OsmoHNBGW -> OsmoMSC [label="IuCS"]; | ||||||
|  |     hNodeB -> OsmoMGW [label="RTP",dir=both]; | ||||||
|  | } | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | [[fig-bsc-e1]] | ||||||
|  | .Integration of legacy E1 BTS in AoIP network | ||||||
|  | [graphviz] | ||||||
|  | ---- | ||||||
|  | digraph G { | ||||||
|  |     rankdir = LR; | ||||||
|  |     BTS -> OsmoBSC [label="Abis/E1"]; | ||||||
|  |     OsmoBSC -> "core-network" [label="3GPP AoIP"]; | ||||||
|  |     OsmoBSC -> OsmoMGW [label="MGCP"]; | ||||||
|  |     BTS -> OsmoMGW [label="TRAU/E1",dir=both]; | ||||||
|  |     OsmoMGW -> "core-network" [label="RTP",dir=both]; | ||||||
|  |     {rank=same OsmoBSC OsmoMGW} | ||||||
|  |     OsmoMGW [color=red]; | ||||||
|  | } | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | === Software Components | ||||||
|  |  | ||||||
|  | OsmoMGW contains a variety of different software components, which we’ll | ||||||
|  | quickly describe in this section. | ||||||
|  |  | ||||||
|  | ==== MGCP Implementation | ||||||
|  |  | ||||||
|  | OsmoMGW brings its own MGCP implementation through which OsmoMGW is | ||||||
|  | controlled. | ||||||
|  |  | ||||||
|  | The commands implemented are CRCX, MDCX, DLCX and RSIP. The command AUEP is | ||||||
|  | implemented as a stub and will simply respond with OK. | ||||||
|  |  | ||||||
|  | ==== RTP implementation | ||||||
|  |  | ||||||
|  | Support for IuUP which is used in 3G cells is quite lacking at the moment. | ||||||
|  | 3G<->3G and 2G<->2G calls should work, but 3G<->2G does not. | ||||||
|  |  | ||||||
|  | ==== Audio transcoder | ||||||
|  |  | ||||||
|  | Transcoding is currently not supported in OsmoMGW. | ||||||
|  |  | ||||||
|  | === Limitations | ||||||
|  |  | ||||||
|  | At the moment (November 2020), OsmoMGW implements RTP proxy / RTP bridge | ||||||
|  | type endpoints and E1/T1 16k/8k sub-slots with TRAU frames for classic BTS | ||||||
|  | support. To the RTP proxy / RTP bridge endpoints two RTP connections can | ||||||
|  | be established, which then work as a tandem. E1/T1 endpoints support one | ||||||
|  | RTP connection at a time that is associated with a sub-slot on an E1 line. | ||||||
|  | We are planning to add further endpoint types for: | ||||||
|  |  | ||||||
|  | - classic E1/T1 timeslots (64kBps alaw/ulaw) | ||||||
|  | - announcement/playout end-points | ||||||
|  | - conference endpoints | ||||||
|  |  | ||||||
|  | === Additional resources | ||||||
|  |  | ||||||
|  | You can find the OsmoMGW issue tracker and wiki online at | ||||||
|  |  | ||||||
|  | - https://osmocom.org/projects/osmomgw | ||||||
|  | - https://osmocom.org/projects/osmomgw/wiki | ||||||
|  |  | ||||||
|  | RFC 3435 for MGCP is located at | ||||||
|  |  | ||||||
|  | - https://tools.ietf.org/html/rfc3435 | ||||||
							
								
								
									
										42
									
								
								doc/manuals/chapters/qos-example.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								doc/manuals/chapters/qos-example.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | ==== Full example of QoS for osmo-mgw | ||||||
|  |  | ||||||
|  | In the below example we will show the full set of configuration required | ||||||
|  | for both DSCP and PCP differentiation of RTP traffic by osmo-mgw. | ||||||
|  |  | ||||||
|  | What we want to achieve in this example is the following configuration: | ||||||
|  |  | ||||||
|  | .DSCP and PCP assignments for osmo-mgw Abis downlink traffic in this example | ||||||
|  | [options="header",width="30%",cols="2,1,1"] | ||||||
|  | |=== | ||||||
|  | |Traffic      |DSCP|PCP | ||||||
|  | |RTP    |  46|  6 | ||||||
|  | |=== | ||||||
|  |  | ||||||
|  | . configure the osmo-mgw program to set the DSCP value | ||||||
|  | . configure an egrees QoS map to map from priority to PCP | ||||||
|  |  | ||||||
|  | .Example Step 1: add related VTY configuration to `osmo-mgw.cfg` | ||||||
|  | ---- | ||||||
|  | ... | ||||||
|  | mgcp | ||||||
|  |  rtp ip-dscp 46 | ||||||
|  |  rtp socket-priority 6 | ||||||
|  | ... | ||||||
|  | ---- | ||||||
|  |  | ||||||
|  | .Example Step 2: egress QoS map to map from socket priority to PCP values | ||||||
|  | ---- | ||||||
|  | $ sudo ip link set dev eth0.9<1> type vlan egress-qos-map 0:0 5:5 6:6 7:7 <2> | ||||||
|  | ---- | ||||||
|  | <1> make sure to specify your specific VLAN interface name here instead of `eth0.9`. | ||||||
|  | <2> create a egress QoS map that maps the priority value 1:1 to the PCP. We also include the | ||||||
|  |     mapping 5:5 and 7:7 from the osmo-bsc example (see <<userman-osmobsc>>) here. | ||||||
|  |  | ||||||
|  | NOTE:: The settings of the `ip` command are volatile and only active until | ||||||
|  | the next reboot (or the network device or VLAN is removed).  Please refer to | ||||||
|  | the documentation of your specific Linux distribution in order to find out how | ||||||
|  | to make such settings persistent by means of an `ifup` hook whenever the interface | ||||||
|  | comes up.  For CentOS/RHEL 8 this can e.g. be achieved by means of an `/sbin/ifup-local | ||||||
|  | script` (when using `network-scripts` and not NetworkManager).  For Debian or Ubuntu, | ||||||
|  | this typically involves adding `up` lines to `/etc/network/interfaces` or a `/etc/network/if-up.d` | ||||||
|  | script. | ||||||
							
								
								
									
										25
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								doc/manuals/chapters/running.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | == Running OsmoMGW | ||||||
|  |  | ||||||
|  | The OsmoMGW executable (`osmo-mgw`) offers the following command-line | ||||||
|  | arguments: | ||||||
|  |  | ||||||
|  | === SYNOPSIS | ||||||
|  |  | ||||||
|  | *osmo-mgw* [-h|-V] [-D] [-c 'CONFIGFILE'] [-s] | ||||||
|  |  | ||||||
|  | === OPTIONS | ||||||
|  |  | ||||||
|  | *-h, --help*:: | ||||||
|  | 	Print a short help message about the supported options | ||||||
|  | *-V, --version*:: | ||||||
|  | 	Print the compile-time version number of the program | ||||||
|  | *-D, --daemonize*:: | ||||||
|  | 	Fork the process as a daemon into background. | ||||||
|  | *-c, --config-file 'CONFIGFILE'*:: | ||||||
|  | 	Specify the file and path name of the configuration file to be | ||||||
|  | 	used. If none is specified, use `osmo-mgw.cfg` in the current | ||||||
|  | 	working directory. | ||||||
|  | *-s, --disable-color*:: | ||||||
|  | 	Disable colors for logging to stderr. This has mostly been | ||||||
|  | 	deprecated by VTY based logging configuration, see <<logging>> | ||||||
|  | 	for more information. | ||||||
							
								
								
									
										47
									
								
								doc/manuals/osmomgw-usermanual-docinfo.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								doc/manuals/osmomgw-usermanual-docinfo.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | <revhistory> | ||||||
|  |   <revision> | ||||||
|  |     <revnumber>1</revnumber> | ||||||
|  |     <date>July 24th, 2018</date> | ||||||
|  |     <authorinitials>DW</authorinitials> | ||||||
|  |     <revremark> | ||||||
|  |       Initial version | ||||||
|  |     </revremark> | ||||||
|  |   </revision> | ||||||
|  | </revhistory> | ||||||
|  |  | ||||||
|  | <authorgroup> | ||||||
|  |   <author> | ||||||
|  |     <firstname>Daniel</firstname> | ||||||
|  |     <surname>Willmann</surname> | ||||||
|  |     <email>dwillmann@sysmocom.de</email> | ||||||
|  |     <authorinitials>DW</authorinitials> | ||||||
|  |     <affiliation> | ||||||
|  |       <shortaffil>sysmocom</shortaffil> | ||||||
|  |       <orgname>sysmocom - s.f.m.c. GmbH</orgname> | ||||||
|  |     </affiliation> | ||||||
|  |   </author> | ||||||
|  | </authorgroup> | ||||||
|  |  | ||||||
|  | <copyright> | ||||||
|  |   <year>2018</year> | ||||||
|  |   <holder>sysmocom - s.f.m.c. GmbH</holder> | ||||||
|  | </copyright> | ||||||
|  |  | ||||||
|  | <legalnotice> | ||||||
|  |   <para> | ||||||
|  | 	Permission is granted to copy, distribute and/or modify this | ||||||
|  | 	document under the terms of the GNU Free Documentation License, | ||||||
|  | 	Version 1.3 or any later version published by the Free Software | ||||||
|  | 	Foundation; with the Invariant Sections being just 'Foreword', | ||||||
|  | 	'Acknowledgements' and 'Preface', with no Front-Cover Texts, | ||||||
|  | 	and no Back-Cover Texts.  A copy of the license is included in | ||||||
|  | 	the section entitled "GNU Free Documentation License". | ||||||
|  |   </para> | ||||||
|  |   <para> | ||||||
|  | 	The Asciidoc source code of this manual can be found at | ||||||
|  | 	<ulink url="http://git.osmocom.org/osmo-gsm-manuals/"> | ||||||
|  | 		http://git.osmocom.org/osmo-gsm-manuals/ | ||||||
|  | 	</ulink> | ||||||
|  |   </para> | ||||||
|  | </legalnotice> | ||||||
|  |  | ||||||
							
								
								
									
										39
									
								
								doc/manuals/osmomgw-usermanual.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								doc/manuals/osmomgw-usermanual.adoc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | :gfdl-enabled: | ||||||
|  | :program-name: OsmoMGW | ||||||
|  |  | ||||||
|  | OsmoMGW User Manual | ||||||
|  | ==================== | ||||||
|  | Daniel Willmann <dwillmann@sysmocom.de> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | include::./common/chapters/preface.adoc[] | ||||||
|  |  | ||||||
|  | include::{srcdir}/chapters/overview.adoc[] | ||||||
|  |  | ||||||
|  | include::{srcdir}/chapters/running.adoc[] | ||||||
|  |  | ||||||
|  | include::./common/chapters/vty.adoc[] | ||||||
|  |  | ||||||
|  | include::./common/chapters/logging.adoc[] | ||||||
|  |  | ||||||
|  | include::{srcdir}/chapters/configuration.adoc[] | ||||||
|  |  | ||||||
|  | include::{srcdir}/chapters/mgcp_endpoints.adoc[] | ||||||
|  |  | ||||||
|  | include::{srcdir}/chapters/mgcp_extensions.adoc[] | ||||||
|  |  | ||||||
|  | include::./common/chapters/osmux/osmux.adoc[] | ||||||
|  |  | ||||||
|  | include::./common/chapters/qos-dscp-pcp.adoc[] | ||||||
|  |  | ||||||
|  | //include::{srcdir}/chapters/counters.adoc[] | ||||||
|  |  | ||||||
|  | include::./common/chapters/vty_cpu_sched.adoc[] | ||||||
|  |  | ||||||
|  | include::./common/chapters/port_numbers.adoc[] | ||||||
|  |  | ||||||
|  | include::./common/chapters/bibliography.adoc[] | ||||||
|  |  | ||||||
|  | include::./common/chapters/glossary.adoc[] | ||||||
|  |  | ||||||
|  | include::./common/chapters/gfdl.adoc[] | ||||||
							
								
								
									
										38
									
								
								doc/manuals/osmomgw-vty-reference.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								doc/manuals/osmomgw-vty-reference.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <!-- | ||||||
|  |   ex:ts=2:sw=42sts=2:et | ||||||
|  |   -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||||||
|  | --> | ||||||
|  | <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN" | ||||||
|  | "http://docbook.org/xml/5.0/dtd/docbook.dtd" [ | ||||||
|  | <!ENTITY chapter-vty      SYSTEM      "./common/chapters/vty.xml" > | ||||||
|  | <!ENTITY sections-vty     SYSTEM      "generated/docbook_vty.xml"  > | ||||||
|  | ]> | ||||||
|  |  | ||||||
|  | <book> | ||||||
|  |   <info> | ||||||
|  |     <revhistory> | ||||||
|  |         <revision> | ||||||
|  |             <revnumber>v1</revnumber> | ||||||
|  |             <date>12th December 2017</date> | ||||||
|  |             <authorinitials>pm</authorinitials> | ||||||
|  |             <revremark>Initial</revremark> | ||||||
|  |         </revision> | ||||||
|  |     </revhistory> | ||||||
|  |  | ||||||
|  |     <title>OsmoMGW VTY Reference</title> | ||||||
|  |  | ||||||
|  |     <copyright> | ||||||
|  |       <year>2017</year> | ||||||
|  |     </copyright> | ||||||
|  |  | ||||||
|  |     <legalnotice> | ||||||
|  |       <para>This work is copyright by <orgname>sysmocom - s.f.m.c. GmbH</orgname>. All rights reserved. | ||||||
|  |       </para> | ||||||
|  |     </legalnotice> | ||||||
|  |   </info> | ||||||
|  |  | ||||||
|  |   <!-- Main chapters--> | ||||||
|  |   &chapter-vty; | ||||||
|  | </book> | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								doc/manuals/regen_doc.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										17
									
								
								doc/manuals/regen_doc.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | #!/bin/sh -x | ||||||
|  |  | ||||||
|  | if [ -z "$DOCKER_PLAYGROUND" ]; then | ||||||
|  | 	echo "You need to set DOCKER_PLAYGROUND" | ||||||
|  | 	exit 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | SCRIPT=$(realpath "$0") | ||||||
|  | MANUAL_DIR=$(dirname "$SCRIPT") | ||||||
|  |  | ||||||
|  | COMMIT=${COMMIT:-$(git log -1 --format=format:%H)} | ||||||
|  |  | ||||||
|  | cd "$DOCKER_PLAYGROUND/scripts" || exit 1 | ||||||
|  |  | ||||||
|  | OSMO_MGW_BRANCH=$COMMIT ./regen_doc.sh osmo-mgw 4243 \ | ||||||
|  | 	"$MANUAL_DIR/chapters/counters_generated.adoc" \ | ||||||
|  | 	"$MANUAL_DIR/vty/mgw_vty_reference.xml" | ||||||
							
								
								
									
										2
									
								
								doc/manuals/vty/mgw_vty_additions.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								doc/manuals/vty/mgw_vty_additions.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | <vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'> | ||||||
|  | </vtydoc> | ||||||
| @@ -92,8 +92,8 @@ fi | |||||||
| if test -n "$v" | if test -n "$v" | ||||||
| then | then | ||||||
|     : # use $v |     : # use $v | ||||||
| elif test -d ./.git \ | elif | ||||||
|     && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ |        v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ | ||||||
| 	  || git describe --abbrev=4 HEAD 2>/dev/null` \ | 	  || git describe --abbrev=4 HEAD 2>/dev/null` \ | ||||||
|     && case $v in |     && case $v in | ||||||
| 	 [0-9]*) ;; | 	 [0-9]*) ;; | ||||||
|   | |||||||
| @@ -3,8 +3,17 @@ SUBDIRS = \ | |||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
| nobase_include_HEADERS = \ | nobase_include_HEADERS = \ | ||||||
| 	osmocom/legacy_mgcp/mgcp.h \ |  | ||||||
| 	osmocom/legacy_mgcp/mgcp_internal.h \ |  | ||||||
| 	osmocom/legacy_mgcp/osmux.h \ |  | ||||||
| 	osmocom/mgcp_client/mgcp_client.h \ | 	osmocom/mgcp_client/mgcp_client.h \ | ||||||
|  | 	osmocom/mgcp_client/mgcp_client_endpoint_fsm.h \ | ||||||
|  | 	osmocom/mgcp_client/mgcp_client_fsm.h \ | ||||||
|  | 	osmocom/mgcp_client/mgcp_client_pool.h \ | ||||||
|  | 	osmocom/mgcp/mgcp.h \ | ||||||
|  | 	osmocom/mgcp/mgcp_common.h \ | ||||||
|  | 	osmocom/mgcp/osmux.h \ | ||||||
|  | 	$(NULL) | ||||||
|  |  | ||||||
|  | # This gets copied during make from osmocom/mgcp/mgcp_common.h. Therefore it is not included in the source tree and we | ||||||
|  | # don't need to distribute it (OS#4084). | ||||||
|  | nobase_nodist_include_HEADERS = \ | ||||||
|  | 	osmocom/mgcp_client/mgcp_common.h \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| SUBDIRS = \ | SUBDIRS = \ | ||||||
| 	legacy_mgcp \ |  | ||||||
| 	mgcp_client \ | 	mgcp_client \ | ||||||
|  | 	mgcp \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|   | |||||||
| @@ -1,4 +0,0 @@ | |||||||
| noinst_HEADERS = \ |  | ||||||
| 	mgcp_transcode.h \ |  | ||||||
| 	vty.h \ |  | ||||||
| 	$(NULL) |  | ||||||
| @@ -1,299 +0,0 @@ | |||||||
| /* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> |  | ||||||
|  * (C) 2009-2012 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 Affero 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 Affero General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU Affero General Public License |  | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #ifndef OPENBSC_MGCP_H |  | ||||||
| #define OPENBSC_MGCP_H |  | ||||||
|  |  | ||||||
| #include <osmocom/core/msgb.h> |  | ||||||
| #include <osmocom/core/write_queue.h> |  | ||||||
| #include <osmocom/core/timer.h> |  | ||||||
| #include <osmocom/core/logging.h> |  | ||||||
|  |  | ||||||
| #include <arpa/inet.h> |  | ||||||
| #include <sys/types.h> |  | ||||||
| #include <sys/socket.h> |  | ||||||
| #include <netinet/in.h> |  | ||||||
|  |  | ||||||
| #define RTP_PORT_DEFAULT 4000 |  | ||||||
| #define RTP_PORT_NET_DEFAULT 16000 |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Calculate the RTP audio port for the given multiplex |  | ||||||
|  * and the direction. This allows a semi static endpoint |  | ||||||
|  * to port calculation removing the need for the BSC |  | ||||||
|  * and the MediaGateway to communicate. |  | ||||||
|  * |  | ||||||
|  * Port usage explained: |  | ||||||
|  *       base + (multiplex * 2) + 0 == local port to wait for network packets |  | ||||||
|  *       base + (multiplex * 2) + 1 == local port for rtcp |  | ||||||
|  * |  | ||||||
|  * The above port will receive packets from the BTS that need |  | ||||||
|  * to be patched and forwarded to the network. |  | ||||||
|  * The above port will receive packets from the network that |  | ||||||
|  * need to be patched and forwarded to the BTS. |  | ||||||
|  * |  | ||||||
|  * We assume to have a static BTS IP address so we can differentiate |  | ||||||
|  * network and BTS. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
| static inline int rtp_calculate_port(int multiplex, int base) |  | ||||||
| { |  | ||||||
| 	return base + (multiplex * 2); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * Handling of MGCP Endpoints and the MGCP Config |  | ||||||
|  */ |  | ||||||
| struct mgcp_endpoint; |  | ||||||
| struct mgcp_config; |  | ||||||
| struct mgcp_trunk_config; |  | ||||||
| struct mgcp_rtp_end; |  | ||||||
|  |  | ||||||
| #define MGCP_ENDP_CRCX 1 |  | ||||||
| #define MGCP_ENDP_DLCX 2 |  | ||||||
| #define MGCP_ENDP_MDCX 3 |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * what to do with the msg? |  | ||||||
|  *	- continue as usual? |  | ||||||
|  *	- reject and send a failure code? |  | ||||||
|  *	- defer? do not send anything |  | ||||||
|  */ |  | ||||||
| #define MGCP_POLICY_CONT	4 |  | ||||||
| #define MGCP_POLICY_REJECT	5 |  | ||||||
| #define MGCP_POLICY_DEFER	6 |  | ||||||
|  |  | ||||||
| typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint); |  | ||||||
| typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state); |  | ||||||
| typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id); |  | ||||||
| typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg); |  | ||||||
| typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone); |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Return: |  | ||||||
|  *   <  0 in case no audio was processed |  | ||||||
|  *   >= 0 in case audio was processed. The remaining payload |  | ||||||
|  *   length will be returned. |  | ||||||
|  */ |  | ||||||
| typedef int (*mgcp_processing)(struct mgcp_endpoint *endp, |  | ||||||
| 			       struct mgcp_rtp_end *dst_end, |  | ||||||
| 			       char *data, int *len, int buf_size); |  | ||||||
| typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp, |  | ||||||
| 				     struct mgcp_rtp_end *dst_end, |  | ||||||
| 				     struct mgcp_rtp_end *src_end); |  | ||||||
|  |  | ||||||
| typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp, |  | ||||||
| 				int *payload_type, |  | ||||||
| 				const char**subtype_name, |  | ||||||
| 				const char**fmtp_extra); |  | ||||||
|  |  | ||||||
| #define PORT_ALLOC_STATIC	0 |  | ||||||
| #define PORT_ALLOC_DYNAMIC	1 |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * This holds information on how to allocate ports |  | ||||||
|  */ |  | ||||||
| struct mgcp_port_range { |  | ||||||
| 	int mode; |  | ||||||
|  |  | ||||||
| 	/* addr or NULL to fall-back to default */ |  | ||||||
| 	char *bind_addr; |  | ||||||
|  |  | ||||||
| 	/* pre-allocated from a base? */ |  | ||||||
| 	int base_port; |  | ||||||
|  |  | ||||||
| 	/* dynamically allocated */ |  | ||||||
| 	int range_start; |  | ||||||
| 	int range_end; |  | ||||||
| 	int last_port; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #define MGCP_KEEPALIVE_ONCE (-1) |  | ||||||
|  |  | ||||||
| struct mgcp_trunk_config { |  | ||||||
| 	struct llist_head entry; |  | ||||||
|  |  | ||||||
| 	struct mgcp_config *cfg; |  | ||||||
|  |  | ||||||
| 	int trunk_nr; |  | ||||||
| 	int trunk_type; |  | ||||||
|  |  | ||||||
| 	char *audio_fmtp_extra; |  | ||||||
| 	char *audio_name; |  | ||||||
| 	int audio_payload; |  | ||||||
| 	int audio_send_ptime; |  | ||||||
| 	int audio_send_name; |  | ||||||
| 	int audio_loop; |  | ||||||
|  |  | ||||||
| 	int no_audio_transcoding; |  | ||||||
|  |  | ||||||
| 	int omit_rtcp; |  | ||||||
| 	int keepalive_interval; |  | ||||||
|  |  | ||||||
| 	/* RTP patching */ |  | ||||||
| 	int force_constant_ssrc; /* 0: don't, 1: once */ |  | ||||||
| 	int force_aligned_timing; |  | ||||||
|  |  | ||||||
| 	/* spec handling */ |  | ||||||
| 	int force_realloc; |  | ||||||
|  |  | ||||||
| 	/* timer */ |  | ||||||
| 	struct osmo_timer_list keepalive_timer; |  | ||||||
|  |  | ||||||
| 	unsigned int number_endpoints; |  | ||||||
| 	struct mgcp_endpoint *endpoints; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| enum mgcp_role { |  | ||||||
| 	MGCP_BSC = 0, |  | ||||||
| 	MGCP_BSC_NAT, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| enum mgcp_connection_mode { |  | ||||||
| 	MGCP_CONN_NONE = 0, |  | ||||||
| 	MGCP_CONN_RECV_ONLY = 1, |  | ||||||
| 	MGCP_CONN_SEND_ONLY = 2, |  | ||||||
| 	MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY, |  | ||||||
| 	MGCP_CONN_LOOPBACK  = 4 | MGCP_CONN_RECV_SEND, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| extern const struct value_string mgcp_connection_mode_strs[]; |  | ||||||
|  |  | ||||||
| static inline const char *mgcp_cmode_name(enum mgcp_connection_mode mode) |  | ||||||
| { |  | ||||||
| 	return get_value_string(mgcp_connection_mode_strs, mode); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct mgcp_config { |  | ||||||
| 	int source_port; |  | ||||||
| 	char *local_ip; |  | ||||||
| 	char *source_addr; |  | ||||||
| 	char *bts_ip; |  | ||||||
| 	char *call_agent_addr; |  | ||||||
|  |  | ||||||
| 	struct in_addr bts_in; |  | ||||||
|  |  | ||||||
| 	/* transcoder handling */ |  | ||||||
| 	char *transcoder_ip; |  | ||||||
| 	struct in_addr transcoder_in; |  | ||||||
| 	int transcoder_remote_base; |  | ||||||
|  |  | ||||||
| 	/* RTP processing */ |  | ||||||
| 	mgcp_processing rtp_processing_cb; |  | ||||||
| 	mgcp_processing_setup setup_rtp_processing_cb; |  | ||||||
|  |  | ||||||
| 	mgcp_get_format get_net_downlink_format_cb; |  | ||||||
|  |  | ||||||
| 	struct osmo_wqueue gw_fd; |  | ||||||
|  |  | ||||||
| 	struct mgcp_port_range bts_ports; |  | ||||||
| 	struct mgcp_port_range net_ports; |  | ||||||
| 	struct mgcp_port_range transcoder_ports; |  | ||||||
| 	int endp_dscp; |  | ||||||
|  |  | ||||||
| 	int bts_force_ptime; |  | ||||||
|  |  | ||||||
| 	mgcp_change change_cb; |  | ||||||
| 	mgcp_policy policy_cb; |  | ||||||
| 	mgcp_reset reset_cb; |  | ||||||
| 	mgcp_realloc realloc_cb; |  | ||||||
| 	mgcp_rqnt rqnt_cb; |  | ||||||
| 	void *data; |  | ||||||
|  |  | ||||||
| 	uint32_t last_call_id; |  | ||||||
|  |  | ||||||
| 	/* trunk handling */ |  | ||||||
| 	struct mgcp_trunk_config trunk; |  | ||||||
| 	struct llist_head trunks; |  | ||||||
|  |  | ||||||
| 	/* only used for start with a static configuration */ |  | ||||||
| 	int last_net_port; |  | ||||||
| 	int last_bts_port; |  | ||||||
|  |  | ||||||
| 	enum mgcp_role role; |  | ||||||
|  |  | ||||||
| 	/* osmux translator: 0 means disabled, 1 means enabled */ |  | ||||||
| 	int osmux; |  | ||||||
| 	/* addr to bind the server to */ |  | ||||||
| 	char *osmux_addr; |  | ||||||
| 	/* The BSC-NAT may ask for enabling osmux on demand. This tells us if |  | ||||||
| 	 * the osmux socket is already initialized. |  | ||||||
| 	 */ |  | ||||||
| 	int osmux_init; |  | ||||||
| 	/* osmux batch factor: from 1 to 4 maximum */ |  | ||||||
| 	int osmux_batch; |  | ||||||
| 	/* osmux batch size (in bytes) */ |  | ||||||
| 	int osmux_batch_size; |  | ||||||
| 	/* osmux port */ |  | ||||||
| 	uint16_t osmux_port; |  | ||||||
| 	/* Pad circuit with dummy messages until we see the first voice |  | ||||||
| 	 * message. |  | ||||||
| 	 */ |  | ||||||
| 	uint16_t osmux_dummy; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /* config management */ |  | ||||||
| struct mgcp_config *mgcp_config_alloc(void); |  | ||||||
| int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, |  | ||||||
| 		      enum mgcp_role role); |  | ||||||
| int mgcp_vty_init(void); |  | ||||||
| int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg); |  | ||||||
| void mgcp_release_endp(struct mgcp_endpoint *endp); |  | ||||||
| void mgcp_initialize_endp(struct mgcp_endpoint *endp); |  | ||||||
| int mgcp_reset_transcoder(struct mgcp_config *cfg); |  | ||||||
| void mgcp_format_stats(struct mgcp_endpoint *endp, char *stats, size_t size); |  | ||||||
| int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter); |  | ||||||
|  |  | ||||||
| void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval); |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * format helper functions |  | ||||||
|  */ |  | ||||||
| struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg); |  | ||||||
|  |  | ||||||
| /* adc helper */ |  | ||||||
| static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot) |  | ||||||
| { |  | ||||||
| 	if (timeslot == 0) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Timeslot should not be 0\n"); |  | ||||||
| 		timeslot = 255; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return timeslot + (32 * multiplex); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void mgcp_endpoint_to_timeslot(int endpoint, int *multiplex, int *timeslot) |  | ||||||
| { |  | ||||||
| 	*multiplex = endpoint / 32; |  | ||||||
| 	*timeslot = endpoint % 32; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint); |  | ||||||
| int mgcp_send_reset_all(struct mgcp_config *cfg); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port); |  | ||||||
| int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, struct sockaddr_in *addr, char *buf, int rc); |  | ||||||
| int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len); |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
| @@ -1,337 +0,0 @@ | |||||||
| /* MGCP Private Data */ |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> |  | ||||||
|  * (C) 2009-2012 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 Affero 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 Affero General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU Affero General Public License |  | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #pragma once |  | ||||||
|  |  | ||||||
| #include <string.h> |  | ||||||
|  |  | ||||||
| #include <osmocom/core/select.h> |  | ||||||
|  |  | ||||||
| #define CI_UNUSED 0 |  | ||||||
|  |  | ||||||
| enum mgcp_trunk_type { |  | ||||||
| 	MGCP_TRUNK_VIRTUAL, |  | ||||||
| 	MGCP_TRUNK_E1, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct mgcp_rtp_stream_state { |  | ||||||
| 	uint32_t ssrc; |  | ||||||
| 	uint16_t last_seq; |  | ||||||
| 	uint32_t last_timestamp; |  | ||||||
| 	uint32_t err_ts_counter; |  | ||||||
| 	int32_t last_tsdelta; |  | ||||||
| 	uint32_t last_arrival_time; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct mgcp_rtp_state { |  | ||||||
| 	int initialized; |  | ||||||
| 	int patch_ssrc; |  | ||||||
|  |  | ||||||
| 	uint32_t orig_ssrc; |  | ||||||
|  |  | ||||||
| 	int seq_offset; |  | ||||||
|  |  | ||||||
| 	int32_t  timestamp_offset; |  | ||||||
| 	uint32_t packet_duration; |  | ||||||
|  |  | ||||||
| 	struct mgcp_rtp_stream_state in_stream; |  | ||||||
| 	struct mgcp_rtp_stream_state out_stream; |  | ||||||
|  |  | ||||||
| 	/* jitter and packet loss calculation */ |  | ||||||
| 	int stats_initialized; |  | ||||||
| 	uint16_t stats_base_seq; |  | ||||||
| 	uint16_t stats_max_seq; |  | ||||||
| 	uint32_t stats_ssrc; |  | ||||||
| 	uint32_t stats_jitter; |  | ||||||
| 	int32_t stats_transit; |  | ||||||
| 	int stats_cycles; |  | ||||||
| 	bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */ |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct mgcp_rtp_codec { |  | ||||||
| 	uint32_t rate; |  | ||||||
| 	int channels; |  | ||||||
| 	uint32_t frame_duration_num; |  | ||||||
| 	uint32_t frame_duration_den; |  | ||||||
|  |  | ||||||
| 	int payload_type; |  | ||||||
| 	char *audio_name; |  | ||||||
| 	char *subtype_name; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct mgcp_rtp_end { |  | ||||||
| 	/* statistics */ |  | ||||||
| 	unsigned int packets; |  | ||||||
| 	unsigned int octets; |  | ||||||
| 	unsigned int dropped_packets; |  | ||||||
| 	struct in_addr addr; |  | ||||||
|  |  | ||||||
| 	/* in network byte order */ |  | ||||||
| 	int rtp_port, rtcp_port; |  | ||||||
|  |  | ||||||
| 	/* audio codec information */ |  | ||||||
| 	struct mgcp_rtp_codec codec; |  | ||||||
| 	struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */ |  | ||||||
|  |  | ||||||
| 	/* per endpoint data */ |  | ||||||
| 	int  frames_per_packet; |  | ||||||
| 	uint32_t packet_duration_ms; |  | ||||||
| 	char *fmtp_extra; |  | ||||||
| 	int output_enabled; |  | ||||||
| 	int force_output_ptime; |  | ||||||
|  |  | ||||||
| 	/* RTP patching */ |  | ||||||
| 	int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ |  | ||||||
| 	int force_aligned_timing; |  | ||||||
| 	void *rtp_process_data; |  | ||||||
|  |  | ||||||
| 	/* |  | ||||||
| 	 * Each end has a socket... |  | ||||||
| 	 */ |  | ||||||
| 	struct osmo_fd rtp; |  | ||||||
| 	struct osmo_fd rtcp; |  | ||||||
|  |  | ||||||
| 	int local_port; |  | ||||||
| 	int local_alloc; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| enum { |  | ||||||
| 	MGCP_TAP_BTS_IN, |  | ||||||
| 	MGCP_TAP_BTS_OUT, |  | ||||||
| 	MGCP_TAP_NET_IN, |  | ||||||
| 	MGCP_TAP_NET_OUT, |  | ||||||
|  |  | ||||||
| 	/* last element */ |  | ||||||
| 	MGCP_TAP_COUNT |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct mgcp_rtp_tap { |  | ||||||
| 	int enabled; |  | ||||||
| 	struct sockaddr_in forward; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct mgcp_lco { |  | ||||||
| 	char *string; |  | ||||||
| 	char *codec; |  | ||||||
| 	int pkt_period_min; /* time in ms */ |  | ||||||
| 	int pkt_period_max; /* time in ms */ |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| enum mgcp_type { |  | ||||||
| 	MGCP_RTP_DEFAULT	= 0, |  | ||||||
| 	MGCP_RTP_TRANSCODED, |  | ||||||
| 	MGCP_OSMUX_BSC, |  | ||||||
| 	MGCP_OSMUX_BSC_NAT, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #include <osmocom/legacy_mgcp/osmux.h> |  | ||||||
|  |  | ||||||
| struct mgcp_endpoint { |  | ||||||
| 	int allocated; |  | ||||||
| 	uint32_t ci; |  | ||||||
| 	char *callid; |  | ||||||
| 	struct mgcp_lco local_options; |  | ||||||
| 	int conn_mode; |  | ||||||
| 	int orig_mode; |  | ||||||
|  |  | ||||||
| 	/* backpointer */ |  | ||||||
| 	struct mgcp_config *cfg; |  | ||||||
| 	struct mgcp_trunk_config *tcfg; |  | ||||||
|  |  | ||||||
| 	/* port status for bts/net */ |  | ||||||
| 	struct mgcp_rtp_end bts_end; |  | ||||||
| 	struct mgcp_rtp_end net_end; |  | ||||||
|  |  | ||||||
| 	/* |  | ||||||
| 	 * For transcoding we will send from the local_port |  | ||||||
| 	 * of trans_bts and it will arrive at trans_net from |  | ||||||
| 	 * where we will forward it to the network. |  | ||||||
| 	 */ |  | ||||||
| 	struct mgcp_rtp_end trans_bts; |  | ||||||
| 	struct mgcp_rtp_end trans_net; |  | ||||||
| 	enum mgcp_type type; |  | ||||||
|  |  | ||||||
| 	/* sequence bits */ |  | ||||||
| 	struct mgcp_rtp_state net_state; |  | ||||||
| 	struct mgcp_rtp_state bts_state; |  | ||||||
|  |  | ||||||
| 	/* fields for re-transmission */ |  | ||||||
| 	char *last_trans; |  | ||||||
| 	char *last_response; |  | ||||||
|  |  | ||||||
| 	/* tap for the endpoint */ |  | ||||||
| 	struct mgcp_rtp_tap taps[MGCP_TAP_COUNT]; |  | ||||||
|  |  | ||||||
| 	struct { |  | ||||||
| 		/* Osmux state: disabled, activating, active */ |  | ||||||
| 		enum osmux_state state; |  | ||||||
| 		/* Allocated Osmux circuit ID for this endpoint */ |  | ||||||
| 		int allocated_cid; |  | ||||||
| 		/* Used Osmux circuit ID for this endpoint */ |  | ||||||
| 		uint8_t cid; |  | ||||||
| 		/* handle to batch messages */ |  | ||||||
| 		struct osmux_in_handle *in; |  | ||||||
| 		/* handle to unbatch messages */ |  | ||||||
| 		struct osmux_out_handle out; |  | ||||||
| 		/* statistics */ |  | ||||||
| 		struct { |  | ||||||
| 			uint32_t chunks; |  | ||||||
| 			uint32_t octets; |  | ||||||
| 		} stats; |  | ||||||
| 	} osmux; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #define for_each_line(line, save)			\ |  | ||||||
| 	for (line = strline_r(NULL, &save); line;\ |  | ||||||
| 	     line = strline_r(NULL, &save)) |  | ||||||
|  |  | ||||||
| static inline char *strline_r(char *str, char **saveptr) |  | ||||||
| { |  | ||||||
| 	char *result; |  | ||||||
|  |  | ||||||
| 	if (str) |  | ||||||
| 		*saveptr = str; |  | ||||||
|  |  | ||||||
| 	result = *saveptr; |  | ||||||
|  |  | ||||||
| 	if (*saveptr != NULL) { |  | ||||||
| 		*saveptr = strpbrk(*saveptr, "\r\n"); |  | ||||||
|  |  | ||||||
| 		if (*saveptr != NULL) { |  | ||||||
| 			char *eos = *saveptr; |  | ||||||
|  |  | ||||||
| 			if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n') |  | ||||||
| 				(*saveptr)++; |  | ||||||
| 			(*saveptr)++; |  | ||||||
| 			if ((*saveptr)[0] == '\0') |  | ||||||
| 				*saveptr = NULL; |  | ||||||
|  |  | ||||||
| 			*eos = '\0'; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints)) |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Internal structure while parsing a request |  | ||||||
|  */ |  | ||||||
| struct mgcp_parse_data { |  | ||||||
| 	struct mgcp_config *cfg; |  | ||||||
| 	struct mgcp_endpoint *endp; |  | ||||||
| 	char *trans; |  | ||||||
| 	char *save; |  | ||||||
| 	int found; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| int mgcp_send_dummy(struct mgcp_endpoint *endp); |  | ||||||
| int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port); |  | ||||||
| int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port); |  | ||||||
| int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *enp, int rtp_port); |  | ||||||
| int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *enp, int rtp_port); |  | ||||||
| int mgcp_free_rtp_port(struct mgcp_rtp_end *end); |  | ||||||
|  |  | ||||||
| /* For transcoding we need to manage an in and an output that are connected */ |  | ||||||
| static inline int endp_back_channel(int endpoint) |  | ||||||
| { |  | ||||||
| 	return endpoint + 60; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index); |  | ||||||
| struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index); |  | ||||||
|  |  | ||||||
| void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, |  | ||||||
| 			 struct mgcp_rtp_end *rtp); |  | ||||||
| uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, |  | ||||||
| 				  struct mgcp_rtp_end *rtp); |  | ||||||
|  |  | ||||||
| void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *, |  | ||||||
| 			uint32_t *expected, int *loss); |  | ||||||
| uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *); |  | ||||||
|  |  | ||||||
| /* payload processing default functions */ |  | ||||||
| int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, |  | ||||||
| 				char *data, int *len, int buf_size); |  | ||||||
|  |  | ||||||
| int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, |  | ||||||
| 				      struct mgcp_rtp_end *dst_end, |  | ||||||
| 				      struct mgcp_rtp_end *src_end); |  | ||||||
|  |  | ||||||
| void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, |  | ||||||
| 					  int *payload_type, |  | ||||||
| 					  const char**subtype_name, |  | ||||||
| 					  const char**fmtp_extra); |  | ||||||
|  |  | ||||||
| /* internal RTP Annex A counting */ |  | ||||||
| void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, |  | ||||||
| 			const uint16_t seq, const int32_t transit, |  | ||||||
| 			const uint32_t ssrc); |  | ||||||
|  |  | ||||||
| int mgcp_set_ip_tos(int fd, int tos); |  | ||||||
|  |  | ||||||
| enum { |  | ||||||
| 	MGCP_DEST_NET = 0, |  | ||||||
| 	MGCP_DEST_BTS, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #define MGCP_DUMMY_LOAD 0x23 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * SDP related information |  | ||||||
|  */ |  | ||||||
| /* Assume audio frame length of 20ms */ |  | ||||||
| #define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20 |  | ||||||
| #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 |  | ||||||
| #define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 |  | ||||||
| #define DEFAULT_RTP_AUDIO_DEFAULT_RATE  8000 |  | ||||||
| #define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1 |  | ||||||
|  |  | ||||||
| #define PTYPE_UNDEFINED (-1) |  | ||||||
| int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p); |  | ||||||
| int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec, |  | ||||||
| 			int payload_type, const char *audio_name); |  | ||||||
|  |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Internal network related |  | ||||||
|  */ |  | ||||||
| static inline const char *mgcp_net_src_addr(struct mgcp_endpoint *endp) |  | ||||||
| { |  | ||||||
| 	if (endp->cfg->net_ports.bind_addr) |  | ||||||
| 		return endp->cfg->net_ports.bind_addr; |  | ||||||
| 	return endp->cfg->source_addr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp) |  | ||||||
| { |  | ||||||
| 	if (endp->cfg->bts_ports.bind_addr) |  | ||||||
| 		return endp->cfg->bts_ports.bind_addr; |  | ||||||
| 	return endp->cfg->source_addr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int mgcp_msg_terminate_nul(struct msgb *msg); |  | ||||||
| @@ -1,90 +0,0 @@ | |||||||
| /* |  | ||||||
|  * (C) 2014 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 Affero 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 Affero General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU Affero General Public License |  | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
| #ifndef OPENBSC_MGCP_TRANSCODE_H |  | ||||||
| #define OPENBSC_MGCP_TRANSCODE_H |  | ||||||
|  |  | ||||||
| #include "bscconfig.h" |  | ||||||
|  |  | ||||||
| #include <gsm.h> |  | ||||||
| #ifdef HAVE_BCG729 |  | ||||||
| #include <bcg729/decoder.h> |  | ||||||
| #include <bcg729/encoder.h> |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| enum audio_format { |  | ||||||
| 	AF_INVALID, |  | ||||||
| 	AF_S16, |  | ||||||
| 	AF_L16, |  | ||||||
| 	AF_GSM, |  | ||||||
| 	AF_G729, |  | ||||||
| 	AF_PCMA, |  | ||||||
| 	AF_PCMU |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| struct mgcp_process_rtp_state { |  | ||||||
| 	/* decoding */ |  | ||||||
| 	enum audio_format src_fmt; |  | ||||||
| 	union { |  | ||||||
| 		gsm gsm_handle; |  | ||||||
| #ifdef HAVE_BCG729 |  | ||||||
| 		bcg729DecoderChannelContextStruct *g729_dec; |  | ||||||
| #endif |  | ||||||
| 	} src; |  | ||||||
| 	size_t src_frame_size; |  | ||||||
| 	size_t src_samples_per_frame; |  | ||||||
|  |  | ||||||
| 	/* processing */ |  | ||||||
|  |  | ||||||
| 	/* encoding */ |  | ||||||
| 	enum audio_format dst_fmt; |  | ||||||
| 	union { |  | ||||||
| 		gsm gsm_handle; |  | ||||||
| #ifdef HAVE_BCG729 |  | ||||||
| 		bcg729EncoderChannelContextStruct *g729_enc; |  | ||||||
| #endif |  | ||||||
| 	} dst; |  | ||||||
| 	size_t dst_frame_size; |  | ||||||
| 	size_t dst_samples_per_frame; |  | ||||||
| 	int dst_packet_duration; |  | ||||||
|  |  | ||||||
| 	int is_running; |  | ||||||
| 	uint16_t next_seq; |  | ||||||
| 	uint32_t next_time; |  | ||||||
| 	int16_t samples[10*160]; |  | ||||||
| 	size_t sample_cnt; |  | ||||||
| 	size_t sample_offs; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| int mgcp_transcoding_setup(struct mgcp_endpoint *endp, |  | ||||||
| 			   struct mgcp_rtp_end *dst_end, |  | ||||||
| 			   struct mgcp_rtp_end *src_end); |  | ||||||
|  |  | ||||||
| void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, |  | ||||||
| 					  int *payload_type, |  | ||||||
| 					  const char**audio_name, |  | ||||||
| 					  const char**fmtp_extra); |  | ||||||
|  |  | ||||||
| int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, |  | ||||||
| 				 struct mgcp_rtp_end *dst_end, |  | ||||||
| 				 char *data, int *len, int buf_size); |  | ||||||
|  |  | ||||||
| int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst); |  | ||||||
| #endif /* OPENBSC_MGCP_TRANSCODE_H */ |  | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| #ifndef _OPENBSC_OSMUX_H_ |  | ||||||
| #define _OPENBSC_OSMUX_H_ |  | ||||||
|  |  | ||||||
| #include <osmocom/netif/osmux.h> |  | ||||||
|  |  | ||||||
| #define OSMUX_PORT	1984 |  | ||||||
|  |  | ||||||
| enum { |  | ||||||
| 	OSMUX_ROLE_BSC = 0, |  | ||||||
| 	OSMUX_ROLE_BSC_NAT, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| int osmux_init(int role, struct mgcp_config *cfg); |  | ||||||
| int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port); |  | ||||||
| void osmux_disable_endpoint(struct mgcp_endpoint *endp); |  | ||||||
| void osmux_allocate_cid(struct mgcp_endpoint *endp); |  | ||||||
| void osmux_release_cid(struct mgcp_endpoint *endp); |  | ||||||
|  |  | ||||||
| int osmux_xfrm_to_rtp(struct mgcp_endpoint *endp, int type, char *buf, int rc); |  | ||||||
| int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp); |  | ||||||
|  |  | ||||||
| int osmux_send_dummy(struct mgcp_endpoint *endp); |  | ||||||
|  |  | ||||||
| int osmux_get_cid(void); |  | ||||||
| void osmux_put_cid(uint8_t osmux_cid); |  | ||||||
| int osmux_used_cid(void); |  | ||||||
|  |  | ||||||
| enum osmux_state { |  | ||||||
| 	OSMUX_STATE_DISABLED = 0, |  | ||||||
| 	OSMUX_STATE_NEGOTIATING, |  | ||||||
| 	OSMUX_STATE_ACTIVATING, |  | ||||||
| 	OSMUX_STATE_ENABLED, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| enum osmux_usage { |  | ||||||
| 	OSMUX_USAGE_OFF = 0, |  | ||||||
| 	OSMUX_USAGE_ON = 1, |  | ||||||
| 	OSMUX_USAGE_ONLY = 2, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
| @@ -1,31 +0,0 @@ | |||||||
| #ifndef OPENBSC_VTY_H |  | ||||||
| #define OPENBSC_VTY_H |  | ||||||
|  |  | ||||||
| #include <osmocom/vty/vty.h> |  | ||||||
| #include <osmocom/vty/buffer.h> |  | ||||||
| #include <osmocom/vty/command.h> |  | ||||||
|  |  | ||||||
| struct gsm_network; |  | ||||||
| struct vty; |  | ||||||
|  |  | ||||||
| void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *); |  | ||||||
|  |  | ||||||
| struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base); |  | ||||||
|  |  | ||||||
| extern struct cmd_element cfg_description_cmd; |  | ||||||
| extern struct cmd_element cfg_no_description_cmd; |  | ||||||
|  |  | ||||||
| enum mgcp_vty_node { |  | ||||||
| 	MGCP_NODE = _LAST_OSMOVTY_NODE + 1, |  | ||||||
| 	TRUNK_NODE, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct log_info; |  | ||||||
| int bsc_vty_init(struct gsm_network *network); |  | ||||||
| int bsc_vty_init_extra(void); |  | ||||||
|  |  | ||||||
| void msc_vty_init(struct gsm_network *msc_network); |  | ||||||
|  |  | ||||||
| struct gsm_network *gsmnet_from_vty(struct vty *vty); |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
							
								
								
									
										16
									
								
								include/osmocom/mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								include/osmocom/mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | noinst_HEADERS = \ | ||||||
|  | 	vty.h \ | ||||||
|  | 	mgcp_msg.h \ | ||||||
|  | 	mgcp_conn.h \ | ||||||
|  | 	mgcp_stat.h \ | ||||||
|  | 	mgcp_endp.h \ | ||||||
|  | 	mgcp_sdp.h \ | ||||||
|  | 	mgcp_codec.h \ | ||||||
|  | 	mgcp_ctrl.h \ | ||||||
|  | 	mgcp_trunk.h \ | ||||||
|  | 	debug.h \ | ||||||
|  | 	mgcp_ratectr.h \ | ||||||
|  | 	mgcp_e1.h \ | ||||||
|  | 	mgcp_network.h \ | ||||||
|  | 	mgcp_protocol.h \ | ||||||
|  | 	$(NULL) | ||||||
							
								
								
									
										36
									
								
								include/osmocom/mgcp/debug.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								include/osmocom/mgcp/debug.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | /* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * Author: Philipp Maier | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <osmocom/core/linuxlist.h> | ||||||
|  |  | ||||||
|  | #define DEBUG | ||||||
|  | #include <osmocom/core/logging.h> | ||||||
|  |  | ||||||
|  | /* Debug Areas of the code */ | ||||||
|  | enum { | ||||||
|  | 	DRTP, | ||||||
|  | 	DE1, | ||||||
|  | 	Debug_LastEntry, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | extern const struct log_info log_info; | ||||||
							
								
								
									
										210
									
								
								include/osmocom/mgcp/mgcp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								include/osmocom/mgcp/mgcp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | |||||||
|  | /* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||||
|  |  * (C) 2009-2012 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 Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <osmocom/core/msgb.h> | ||||||
|  | #include <osmocom/core/socket.h> | ||||||
|  | #include <osmocom/core/write_queue.h> | ||||||
|  | #include <osmocom/core/timer.h> | ||||||
|  | #include <osmocom/core/logging.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/mgcp/mgcp_common.h> | ||||||
|  |  | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <netinet/in.h> | ||||||
|  | #include <pthread.h> | ||||||
|  |  | ||||||
|  | #include "mgcp_ratectr.h" | ||||||
|  |  | ||||||
|  | #define RTP_PORT_DEFAULT_RANGE_START 16002 | ||||||
|  | #define RTP_PORT_DEFAULT_RANGE_END RTP_PORT_DEFAULT_RANGE_START + 64 | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * Handling of MGCP Endpoints and the MGCP Config | ||||||
|  |  */ | ||||||
|  | struct mgcp_endpoint; | ||||||
|  | struct mgcp_config; | ||||||
|  | struct mgcp_trunk; | ||||||
|  | struct mgcp_rtp_end; | ||||||
|  |  | ||||||
|  | #define MGCP_ENDP_CRCX 1 | ||||||
|  | #define MGCP_ENDP_DLCX 2 | ||||||
|  | #define MGCP_ENDP_MDCX 3 | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * what to do with the msg? | ||||||
|  |  *	- continue as usual? | ||||||
|  |  *	- reject and send a failure code? | ||||||
|  |  *	- defer? do not send anything | ||||||
|  |  */ | ||||||
|  | #define MGCP_POLICY_CONT	4 | ||||||
|  | #define MGCP_POLICY_REJECT	5 | ||||||
|  | #define MGCP_POLICY_DEFER	6 | ||||||
|  |  | ||||||
|  | typedef int (*mgcp_reset)(struct mgcp_trunk *cfg); | ||||||
|  | typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Return: | ||||||
|  |  *   <  0 in case no audio was processed | ||||||
|  |  *   >= 0 in case audio was processed. The remaining payload | ||||||
|  |  *   length will be returned. | ||||||
|  |  */ | ||||||
|  | typedef int (*mgcp_processing)(struct mgcp_endpoint *endp, | ||||||
|  | 			       struct mgcp_rtp_end *dst_end, | ||||||
|  | 			       char *data, int *len, int buf_size); | ||||||
|  |  | ||||||
|  | struct mgcp_conn_rtp; | ||||||
|  |  | ||||||
|  | typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp, | ||||||
|  | 				     struct mgcp_conn_rtp *conn_dst, | ||||||
|  | 				     struct mgcp_conn_rtp *conn_src); | ||||||
|  |  | ||||||
|  | struct mgcp_rtp_codec; | ||||||
|  |  | ||||||
|  | typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp, | ||||||
|  | 				const struct mgcp_rtp_codec **codec, | ||||||
|  | 				const char **fmtp_extra, | ||||||
|  | 				struct mgcp_conn_rtp *conn); | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This holds information on how to allocate ports | ||||||
|  |  */ | ||||||
|  | struct mgcp_port_range { | ||||||
|  | 	pthread_mutex_t lock; | ||||||
|  | 	/* addr or NULL to fall-back to default */ | ||||||
|  | 	char bind_addr_v4[INET6_ADDRSTRLEN]; | ||||||
|  | 	char bind_addr_v6[INET6_ADDRSTRLEN]; | ||||||
|  |  | ||||||
|  | 	/* dynamically allocated */ | ||||||
|  | 	int range_start; | ||||||
|  | 	int range_end; | ||||||
|  | 	int last_port; | ||||||
|  |  | ||||||
|  | 	/* set to true to enable automatic probing | ||||||
|  | 	 * of the local bind IP-Address, bind_addr | ||||||
|  | 	 * (or its fall back) is used when automatic | ||||||
|  | 	 * probing fails */ | ||||||
|  | 	bool bind_addr_probe; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* There are up to three modes in which the keep-alive dummy packet can be | ||||||
|  |  * sent. The behaviour is controlled via the keepalive_interval member of the | ||||||
|  |  * trunk config. If that member is set to 0 (MGCP_KEEPALIVE_NEVER) no dummy- | ||||||
|  |  * packet is sent at all and the timer that sends regular dummy packets | ||||||
|  |  * is no longer scheduled. If the keepalive_interval is set to -1, only | ||||||
|  |  * one dummy packet is sent when an CRCX or an MDCX is performed. No timer | ||||||
|  |  * is scheduled. For all vales greater 0, the timer is scheduled and the | ||||||
|  |  * value is used as interval. See also mgcp_keepalive_timer_cb(), | ||||||
|  |  * handle_modify_con(), and handle_create_con() */ | ||||||
|  | #define MGCP_KEEPALIVE_ONCE (-1) | ||||||
|  | #define MGCP_KEEPALIVE_NEVER 0 | ||||||
|  |  | ||||||
|  | enum mgcp_role { | ||||||
|  | 	MGCP_BSC = 0, | ||||||
|  | 	MGCP_BSC_NAT, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct mgcp_config { | ||||||
|  | 	int source_port; | ||||||
|  | 	char local_ip[INET6_ADDRSTRLEN]; | ||||||
|  | 	char source_addr[INET6_ADDRSTRLEN]; | ||||||
|  | 	char call_agent_addr[INET6_ADDRSTRLEN]; | ||||||
|  |  | ||||||
|  | 	/* RTP processing */ | ||||||
|  | 	mgcp_processing rtp_processing_cb; | ||||||
|  | 	mgcp_processing_setup setup_rtp_processing_cb; | ||||||
|  |  | ||||||
|  | 	mgcp_get_format get_net_downlink_format_cb; | ||||||
|  |  | ||||||
|  | 	struct osmo_wqueue gw_fd; | ||||||
|  |  | ||||||
|  | 	struct mgcp_port_range net_ports; | ||||||
|  | 	int endp_dscp; | ||||||
|  | 	int endp_priority; | ||||||
|  |  | ||||||
|  | 	int force_ptime; | ||||||
|  |  | ||||||
|  | 	mgcp_reset reset_cb; | ||||||
|  | 	mgcp_rqnt rqnt_cb; | ||||||
|  | 	void *data; | ||||||
|  |  | ||||||
|  | 	/* list holding the trunks */ | ||||||
|  | 	struct llist_head trunks; | ||||||
|  |  | ||||||
|  | 	enum mgcp_role role; | ||||||
|  |  | ||||||
|  | 	/* osmux translator: 0 means disabled, 1 means enabled */ | ||||||
|  | 	int osmux; | ||||||
|  | 	/* addr to bind the server to */ | ||||||
|  | 	char osmux_addr[INET6_ADDRSTRLEN]; | ||||||
|  | 	/* The BSC-NAT may ask for enabling osmux on demand. This tells us if | ||||||
|  | 	 * the osmux socket is already initialized. | ||||||
|  | 	 */ | ||||||
|  | 	int osmux_init; | ||||||
|  | 	/* osmux batch factor: from 1 to 4 maximum */ | ||||||
|  | 	int osmux_batch; | ||||||
|  | 	/* osmux batch size (in bytes) */ | ||||||
|  | 	int osmux_batch_size; | ||||||
|  | 	/* osmux port */ | ||||||
|  | 	uint16_t osmux_port; | ||||||
|  | 	/* Pad circuit with dummy messages until we see the first voice | ||||||
|  | 	 * message. | ||||||
|  | 	 */ | ||||||
|  | 	uint16_t osmux_dummy; | ||||||
|  | 	/* domain name of the media gateway */ | ||||||
|  | 	char domain[255+1]; | ||||||
|  |  | ||||||
|  | 	/* time after which inactive connections (CIs) get closed */ | ||||||
|  | 	int conn_timeout; | ||||||
|  |  | ||||||
|  | 	/* osmocom CTRL interface */ | ||||||
|  | 	struct ctrl_handle *ctrl; | ||||||
|  |  | ||||||
|  | 	/* global rate counters to measure the MGWs overall performance and | ||||||
|  | 	 * health */ | ||||||
|  | 	struct mgcp_ratectr_global ratectr; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* config management */ | ||||||
|  | struct mgcp_config *mgcp_config_alloc(void); | ||||||
|  | int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, | ||||||
|  | 		      enum mgcp_role role); | ||||||
|  | int mgcp_vty_init(void); | ||||||
|  | void mgcp_trunk_set_keepalive(struct mgcp_trunk *trunk, int interval); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * format helper functions | ||||||
|  |  */ | ||||||
|  | struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int mgcp_send_reset_ep(struct mgcp_endpoint *endp); | ||||||
|  | int mgcp_send_reset_all(struct mgcp_config *cfg); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port, uint8_t dscp, | ||||||
|  | 		     uint8_t prio); | ||||||
|  | int mgcp_udp_send(int fd, struct osmo_sockaddr *addr, int port, const char *buf, int len); | ||||||
							
								
								
									
										20
									
								
								include/osmocom/mgcp/mgcp_codec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								include/osmocom/mgcp/mgcp_codec.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20 | ||||||
|  | #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 | ||||||
|  | #define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 | ||||||
|  | #define DEFAULT_RTP_AUDIO_DEFAULT_RATE  8000 | ||||||
|  | #define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1 | ||||||
|  |  | ||||||
|  | #define PTYPE_UNDEFINED (-1) | ||||||
|  |  | ||||||
|  | struct mgcp_conn_rtp; | ||||||
|  |  | ||||||
|  | void mgcp_codec_summary(struct mgcp_conn_rtp *conn); | ||||||
|  | void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn); | ||||||
|  | int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param); | ||||||
|  | int mgcp_codec_decide(struct mgcp_conn_rtp *conn); | ||||||
|  | int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type); | ||||||
|  | const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn, | ||||||
|  | 								const char *subtype_name, unsigned int match_nr); | ||||||
|  | bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec); | ||||||
							
								
								
									
										111
									
								
								include/osmocom/mgcp/mgcp_common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								include/osmocom/mgcp/mgcp_common.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | /* MGCP common implementations. | ||||||
|  |  * These are used in libosmo-mgcp as well as libosmo-mgcp-client. | ||||||
|  |  * To avoid interdependency, these are implemented in .h file only. */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||||
|  |  * (C) 2009-2012 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 Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | /* Two copies of this file are kept in osmocom/mgcp/ and osmocom/mgcp_client/. | ||||||
|  |  * Since both are by definition identical, use the old header exclusion ifdefs | ||||||
|  |  * instead of '#pragma once' to avoid including both of these files. | ||||||
|  |  * Though at the time of writing there are no such users, this allows including | ||||||
|  |  * both libosmo-mgcp and libosmo-mgcp-client headers in the same file. */ | ||||||
|  | #ifndef OSMO_MGCP_COMMON_H | ||||||
|  | #define OSMO_MGCP_COMMON_H | ||||||
|  |  | ||||||
|  | #include <string.h> | ||||||
|  | #include <errno.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/core/msgb.h> | ||||||
|  | #include <osmocom/core/logging.h> | ||||||
|  |  | ||||||
|  | #define for_each_non_empty_line(line, save)			\ | ||||||
|  | 	for (line = strtok_r(NULL, "\r\n", &save); line;	\ | ||||||
|  | 	     line = strtok_r(NULL, "\r\n", &save)) | ||||||
|  |  | ||||||
|  | enum mgcp_connection_mode { | ||||||
|  | 	MGCP_CONN_NONE = 0, | ||||||
|  | 	MGCP_CONN_RECV_ONLY = 1, | ||||||
|  | 	MGCP_CONN_SEND_ONLY = 2, | ||||||
|  | 	MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY, | ||||||
|  | 	MGCP_CONN_LOOPBACK  = 4 | MGCP_CONN_RECV_SEND, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define MGCP_X_OSMO_IGN_HEADER "X-Osmo-IGN:" | ||||||
|  | #define MGCP_X_OSMO_OSMUX_HEADER "X-Osmux:" | ||||||
|  |  | ||||||
|  | /* Values should be bitwise-OR-able */ | ||||||
|  | enum mgcp_x_osmo_ign { | ||||||
|  | 	MGCP_X_OSMO_IGN_NONE = 0, | ||||||
|  | 	MGCP_X_OSMO_IGN_CALLID = 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Codec parameters (communicated via SDP/fmtp) */ | ||||||
|  | struct mgcp_codec_param { | ||||||
|  | 	bool amr_octet_aligned_present; | ||||||
|  | 	bool amr_octet_aligned; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Ensure that the msg->l2h is NUL terminated. */ | ||||||
|  | static inline int mgcp_msg_terminate_nul(struct msgb *msg) | ||||||
|  | { | ||||||
|  | 	unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */ | ||||||
|  | 	if (tail[-1] == '\0') | ||||||
|  | 		/* nothing to do */; | ||||||
|  | 	else if (msgb_tailroom(msg) > 0) | ||||||
|  | 		tail[0] = '\0'; | ||||||
|  | 	else if (tail[-1] == '\r' || tail[-1] == '\n') | ||||||
|  | 		tail[-1] = '\0'; | ||||||
|  | 	else { | ||||||
|  | 		LOGP(DLMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " | ||||||
|  | 		     "Length: %d, Buffer size: %d\n", | ||||||
|  | 		     msgb_l2len(msg), msg->data_len); | ||||||
|  | 		return -ENOTSUP; | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Maximum length of the comment field */ | ||||||
|  | #define MGCP_COMMENT_MAXLEN 256 | ||||||
|  |  | ||||||
|  | /* Maximum allowed String length of Connection Identifiers as per spec | ||||||
|  |  * (see also RFC3435 2.1.3.2 Names of Connections), plus one for '\0'. */ | ||||||
|  | #define MGCP_CONN_ID_MAXLEN 32+1 | ||||||
|  |  | ||||||
|  | /* Deprecated: old name of MGCP_CONN_ID_MAXLEN. */ | ||||||
|  | #define MGCP_CONN_ID_LENGTH MGCP_CONN_ID_MAXLEN | ||||||
|  |  | ||||||
|  | /* String length of Endpoint Identifiers. | ||||||
|  | /  (see also RFC3435 section 3.2.1.3) */ | ||||||
|  | #define MGCP_ENDPOINT_MAXLEN (255*2+1+1) | ||||||
|  |  | ||||||
|  | /* A prefix to denote the virtual trunk (RTP on both ends) */ | ||||||
|  | #define MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK "rtpbridge/" | ||||||
|  |  | ||||||
|  | /* A prefix to denote the e1 trunk | ||||||
|  |  * (see also RFC3435 section E.2) */ | ||||||
|  | #define MGCP_ENDPOINT_PREFIX_E1_TRUNK "ds/e1-" | ||||||
|  |  | ||||||
|  | /* Maximal number of payload types / codecs that can be negotiated via SDP at | ||||||
|  |  * at once. */ | ||||||
|  | #define MGCP_MAX_CODECS 10 | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										190
									
								
								include/osmocom/mgcp/mgcp_conn.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								include/osmocom/mgcp/mgcp_conn.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,190 @@ | |||||||
|  | /* Message connection list handling */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * Author: Philipp Maier | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <osmocom/mgcp/mgcp.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_network.h> | ||||||
|  | #include <osmocom/mgcp/osmux.h> | ||||||
|  | #include <osmocom/core/linuxlist.h> | ||||||
|  | #include <osmocom/core/rate_ctr.h> | ||||||
|  | #include <inttypes.h> | ||||||
|  |  | ||||||
|  | #define LOGPCONN(conn, cat, level, fmt, args...) \ | ||||||
|  | LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \ | ||||||
|  |          (conn)->id, \ | ||||||
|  |          ## args) | ||||||
|  |  | ||||||
|  | #define LOG_CONN(conn, level, fmt, args...) \ | ||||||
|  | 	LOGP(DRTP, level, "(%s I:%s) " fmt, \ | ||||||
|  | 	     (conn)->endp ? (conn)->endp->name : "none", (conn)->id, ## args) | ||||||
|  |  | ||||||
|  | #define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \ | ||||||
|  | 	LOG_CONN((conn_rtp)->conn, level, fmt, ## args) | ||||||
|  |  | ||||||
|  | /* Specific rtp connection type (see struct mgcp_conn_rtp) */ | ||||||
|  | enum mgcp_conn_rtp_type { | ||||||
|  | 	MGCP_RTP_DEFAULT	= 0, | ||||||
|  | 	MGCP_OSMUX_BSC, | ||||||
|  | 	MGCP_OSMUX_BSC_NAT, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /*! Connection type, specifies which member of the union "u" in mgcp_conn | ||||||
|  |  *  contains a useful connection description (currently only RTP) */ | ||||||
|  | enum mgcp_conn_type { | ||||||
|  | 	MGCP_CONN_TYPE_RTP, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* MGCP connection (RTP) */ | ||||||
|  | struct mgcp_conn_rtp { | ||||||
|  |  | ||||||
|  | 	/* Backpointer to conn struct */ | ||||||
|  | 	struct mgcp_conn *conn; | ||||||
|  |  | ||||||
|  | 	/* Specific connection type */ | ||||||
|  | 	enum mgcp_conn_rtp_type type; | ||||||
|  |  | ||||||
|  | 	/* Port status */ | ||||||
|  | 	struct mgcp_rtp_end end; | ||||||
|  |  | ||||||
|  | 	/* Sequence bits */ | ||||||
|  | 	struct mgcp_rtp_state state; | ||||||
|  |  | ||||||
|  | 	/* taps for the rtp connection; one per direction */ | ||||||
|  | 	struct mgcp_rtp_tap tap_in; | ||||||
|  | 	struct mgcp_rtp_tap tap_out; | ||||||
|  |  | ||||||
|  | 	/* Osmux states (optional) */ | ||||||
|  | 	struct { | ||||||
|  | 		/* Osmux state: disabled, activating, active */ | ||||||
|  | 		enum osmux_state state; | ||||||
|  | 		/* Is cid holding valid data? is it allocated from pool? */ | ||||||
|  | 		bool cid_allocated; | ||||||
|  | 		/* Allocated Osmux circuit ID for this conn */ | ||||||
|  | 		uint8_t cid; | ||||||
|  | 		/* handle to batch messages */ | ||||||
|  | 		struct osmux_in_handle *in; | ||||||
|  | 		/* handle to unbatch messages */ | ||||||
|  | 		struct osmux_out_handle out; | ||||||
|  | 		/* statistics */ | ||||||
|  | 		struct { | ||||||
|  | 			uint32_t chunks; | ||||||
|  | 			uint32_t octets; | ||||||
|  | 		} stats; | ||||||
|  | 	} osmux; | ||||||
|  |  | ||||||
|  | 	struct rate_ctr_group *rate_ctr_group; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /*! MGCP connection (untyped) */ | ||||||
|  | struct mgcp_conn { | ||||||
|  | 	/*! list head */ | ||||||
|  | 	struct llist_head entry; | ||||||
|  |  | ||||||
|  | 	/*! Backpointer to the endpoint where the conn belongs to */ | ||||||
|  | 	struct mgcp_endpoint *endp; | ||||||
|  |  | ||||||
|  | 	/*! type of the connection (union) */ | ||||||
|  | 	enum mgcp_conn_type type; | ||||||
|  |  | ||||||
|  | 	/*! mode of the connection */ | ||||||
|  | 	enum mgcp_connection_mode mode; | ||||||
|  |  | ||||||
|  | 	/*! copy of the mode to restore the original setting (VTY) */ | ||||||
|  | 	enum mgcp_connection_mode mode_orig; | ||||||
|  |  | ||||||
|  | 	/*! connection id to identify the connection */ | ||||||
|  | 	char id[MGCP_CONN_ID_MAXLEN]; | ||||||
|  |  | ||||||
|  | 	/*! human readable name (vty, logging) */ | ||||||
|  | 	char name[256]; | ||||||
|  |  | ||||||
|  | 	/*! activity tracker (for cleaning up inactive connections) */ | ||||||
|  | 	struct osmo_timer_list watchdog; | ||||||
|  |  | ||||||
|  | 	/*! union with connection description */ | ||||||
|  | 	union { | ||||||
|  | 		struct mgcp_conn_rtp rtp; | ||||||
|  | 	} u; | ||||||
|  |  | ||||||
|  | 	/*! pointer to optional private data */ | ||||||
|  | 	void *priv; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* RTP connection related counters */ | ||||||
|  | enum { | ||||||
|  | 	IN_STREAM_ERR_TSTMP_CTR, | ||||||
|  | 	OUT_STREAM_ERR_TSTMP_CTR, | ||||||
|  |         RTP_PACKETS_RX_CTR, | ||||||
|  |         RTP_OCTETS_RX_CTR, | ||||||
|  |         RTP_PACKETS_TX_CTR, | ||||||
|  |         RTP_OCTETS_TX_CTR, | ||||||
|  |         RTP_DROPPED_PACKETS_CTR, | ||||||
|  |         RTP_NUM_CONNECTIONS, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* RTP per-connection statistics. Instances of the corresponding rate counter group | ||||||
|  |  * exist for the lifetime of an RTP connection. | ||||||
|  |  * Must be kept in sync with all_rtp_conn_rate_ctr_desc below */ | ||||||
|  | static const struct rate_ctr_desc mgcp_conn_rate_ctr_desc[] = { | ||||||
|  | 	[IN_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:in", "Inbound rtp-stream timestamp errors."}, | ||||||
|  | 	[OUT_STREAM_ERR_TSTMP_CTR] = {"stream_err_tstmp:out", "Outbound rtp-stream timestamp errors."}, | ||||||
|  | 	[RTP_PACKETS_RX_CTR] = {"rtp:packets_rx", "Inbound rtp packets."}, | ||||||
|  | 	[RTP_OCTETS_RX_CTR] = {"rtp:octets_rx", "Inbound rtp octets."}, | ||||||
|  | 	[RTP_PACKETS_TX_CTR] = {"rtp:packets_tx", "Outbound rtp packets."}, | ||||||
|  | 	[RTP_OCTETS_TX_CTR] = {"rtp:octets_tx", "Outbound rtp octets."}, | ||||||
|  | 	[RTP_DROPPED_PACKETS_CTR] = {"rtp:dropped", "dropped rtp packets."} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Aggregated RTP connection stats. These are updated when an RTP connection is freed. | ||||||
|  |  * Must be kept in sync with mgcp_conn_rate_ctr_desc above */ | ||||||
|  | static const struct rate_ctr_desc all_rtp_conn_rate_ctr_desc[] = { | ||||||
|  | 	[IN_STREAM_ERR_TSTMP_CTR] = {"all_rtp:err_tstmp_in", "Total inbound rtp-stream timestamp errors."}, | ||||||
|  | 	[OUT_STREAM_ERR_TSTMP_CTR] = {"all_rtp:err_tstmp_out", "Total outbound rtp-stream timestamp errors."}, | ||||||
|  | 	[RTP_PACKETS_RX_CTR] = {"all_rtp:packets_rx", "Total inbound rtp packets."}, | ||||||
|  | 	[RTP_OCTETS_RX_CTR] = {"all_rtp:octets_rx", "Total inbound rtp octets."}, | ||||||
|  | 	[RTP_PACKETS_TX_CTR] = {"all_rtp:packets_tx", "Total outbound rtp packets."}, | ||||||
|  | 	[RTP_OCTETS_TX_CTR] = {"all_rtp:octets_tx", "Total outbound rtp octets."}, | ||||||
|  | 	[RTP_DROPPED_PACKETS_CTR] = {"all_rtp:dropped", "Total dropped rtp packets."}, | ||||||
|  |  | ||||||
|  | 	/* This last counter does not exist in per-connection stats, only here. */ | ||||||
|  | 	[RTP_NUM_CONNECTIONS] = {"all_rtp:num_closed_conns", "Total number of rtp connections closed."} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Was conn configured to handle Osmux? */ | ||||||
|  | static inline bool mgcp_conn_rtp_is_osmux(const struct mgcp_conn_rtp *conn) { | ||||||
|  | 	return conn->type == MGCP_OSMUX_BSC || conn->type == MGCP_OSMUX_BSC_NAT; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, | ||||||
|  | 				  enum mgcp_conn_type type, char *name); | ||||||
|  | struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id); | ||||||
|  | struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, | ||||||
|  | 					const char *id); | ||||||
|  | void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id); | ||||||
|  | void mgcp_conn_free_oldest(struct mgcp_endpoint *endp); | ||||||
|  | void mgcp_conn_free_all(struct mgcp_endpoint *endp); | ||||||
|  | char *mgcp_conn_dump(struct mgcp_conn *conn); | ||||||
|  | struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn); | ||||||
|  | struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp); | ||||||
|  | void mgcp_conn_watchdog_kick(struct mgcp_conn *conn); | ||||||
							
								
								
									
										24
									
								
								include/osmocom/mgcp/mgcp_ctrl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								include/osmocom/mgcp/mgcp_ctrl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | /* | ||||||
|  |  * (C) 2020 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 Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg, | ||||||
|  |                                              const char *bind_addr, uint16_t port); | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								include/osmocom/mgcp/mgcp_e1.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								include/osmocom/mgcp/mgcp_e1.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | /* A 64k timeslot on an E1 line can be subdevied into the following | ||||||
|  |  * subslot combinations: | ||||||
|  |  * | ||||||
|  |  * subslot:                                          offset: | ||||||
|  |  * [          ][          ][   16k    ][8k_subslot]  0 | ||||||
|  |  * [          ][   32k    ][_subslot__][8k_subslot]  1 | ||||||
|  |  * [          ][ subslot  ][   16k    ][8k_subslot]  2 | ||||||
|  |  * [   64k    ][__________][_subslot__][8k_subslot]  3 | ||||||
|  |  * [ timeslot ][          ][   16k    ][8k_subslot]  4 | ||||||
|  |  * [          ][   32K    ][_subslot__][8k_subslot]  5 | ||||||
|  |  * [          ][ subslot  ][   16k    ][8k_subslot]  6 | ||||||
|  |  * [          ][          ][ subslot  ][8k_subslot]  7 | ||||||
|  |  * | ||||||
|  |  * Since overlapping assignment of subslots is not possible there is a limited | ||||||
|  |  * set of subslot assignments possible. The e1_rates array lists the possible | ||||||
|  |  * assignments as depicted above. Also each subslot assignment comes along with | ||||||
|  |  * a bit offset in the E1 bitstream. The e1_offsets arrays lists the bit | ||||||
|  |  * offsets. */ | ||||||
|  | static const uint8_t e1_rates[] = { 64, 32, 32, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8 }; | ||||||
|  | static const uint8_t e1_offsets[] = { 0, 0, 4, 0, 2, 4, 6, 0, 1, 2, 3, 4, 5, 6, 7 }; | ||||||
|  |  | ||||||
|  | int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs); | ||||||
|  | void mgcp_e1_endp_update(struct mgcp_endpoint *endp); | ||||||
|  | void mgcp_e1_endp_release(struct mgcp_endpoint *endp); | ||||||
|  | int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg); | ||||||
							
								
								
									
										142
									
								
								include/osmocom/mgcp/mgcp_endp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								include/osmocom/mgcp/mgcp_endp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,142 @@ | |||||||
|  | /* Endpoint types */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * Author: Philipp Maier | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <osmocom/core/msgb.h> | ||||||
|  | #include <osmocom/gsm/i460_mux.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_protocol.h> | ||||||
|  |  | ||||||
|  | struct sockaddr; | ||||||
|  | struct mgcp_conn; | ||||||
|  | struct mgcp_conn_rtp; | ||||||
|  | struct mgcp_endpoint; | ||||||
|  |  | ||||||
|  | /* Number of E1 subslots (different variants, not all useable at the same time) */ | ||||||
|  | #define MGCP_ENDP_E1_SUBSLOTS 15 | ||||||
|  |  | ||||||
|  | #define LOGPENDP(endp, cat, level, fmt, args...) \ | ||||||
|  | LOGP(cat, level, "endpoint:%s " fmt, \ | ||||||
|  |      endp ? endp->name : "none", \ | ||||||
|  |      ## args) | ||||||
|  |  | ||||||
|  | struct osmo_rtp_msg_ctx { | ||||||
|  | 	int proto; | ||||||
|  | 	struct mgcp_conn_rtp *conn_src; | ||||||
|  | 	struct osmo_sockaddr *from_addr; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define OSMO_RTP_MSG_CTX(MSGB) ((struct osmo_rtp_msg_ctx*)(MSGB)->cb) | ||||||
|  |  | ||||||
|  | osmo_static_assert(sizeof(((struct msgb*)0)->cb) >= sizeof(struct osmo_rtp_msg_ctx), osmo_rtp_msg_ctx_fits_in_msgb_cb); | ||||||
|  |  | ||||||
|  | /* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below). | ||||||
|  |  * The OSMO_RTP_MSG_CTX() should be set appropriately on the msg. */ | ||||||
|  | typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg); | ||||||
|  |  | ||||||
|  | /* Callback type for endpoint specific cleanup actions. This function | ||||||
|  |  * is automatically executed when a connection is freed (see mgcp_conn_free() | ||||||
|  |  * in mgcp_conn.c). Depending on the type of the endpoint there may be endpoint | ||||||
|  |  * specific things to take care of once a connection has been removed. */ | ||||||
|  | typedef void (*mgcp_cleanup_cp) (struct mgcp_endpoint *endp, | ||||||
|  | 				 struct mgcp_conn *conn); | ||||||
|  |  | ||||||
|  | /*! MGCP endpoint properties */ | ||||||
|  | struct mgcp_endpoint_type { | ||||||
|  | 	/*! maximum number of connections */ | ||||||
|  | 	int max_conns; | ||||||
|  |  | ||||||
|  | 	/*! callback that defines how to dispatch incoming RTP data */ | ||||||
|  | 	mgcp_dispatch_rtp_cb dispatch_rtp_cb; | ||||||
|  |  | ||||||
|  | 	/*! callback that implements endpoint specific cleanup actions */ | ||||||
|  | 	mgcp_cleanup_cp cleanup_cb; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /*! MGCP endpoint typeset */ | ||||||
|  | struct mgcp_endpoint_typeset { | ||||||
|  | 	struct mgcp_endpoint_type rtp; | ||||||
|  | 	struct mgcp_endpoint_type e1; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /*! static MGCP endpoint typeset (pre-initalized, read-only) */ | ||||||
|  | extern const struct mgcp_endpoint_typeset ep_typeset; | ||||||
|  |  | ||||||
|  | /*! MGCP endpoint model */ | ||||||
|  | struct mgcp_endpoint { | ||||||
|  |  | ||||||
|  | 	/*! Unique endpoint name, used for addressing via MGCP */ | ||||||
|  | 	char *name; | ||||||
|  |  | ||||||
|  | 	/*! Call identifier string (as supplied by the call agant) */ | ||||||
|  | 	char *callid; | ||||||
|  |  | ||||||
|  | 	/*! Local connection options (see mgcp_internal.h) */ | ||||||
|  | 	struct mgcp_lco local_options; | ||||||
|  |  | ||||||
|  | 	/*! List of struct mgcp_conn, of the connections active on this endpoint */ | ||||||
|  | 	struct llist_head conns; | ||||||
|  |  | ||||||
|  | 	/*! Backpointer to the trunk this endpoint belongs to */ | ||||||
|  | 	struct mgcp_trunk *trunk; | ||||||
|  |  | ||||||
|  | 	/*! Endpoint properties (see above) */ | ||||||
|  | 	const struct mgcp_endpoint_type *type; | ||||||
|  |  | ||||||
|  | 	/*! Last MGCP transmission (in case re-transmission is required) */ | ||||||
|  | 	char *last_trans; | ||||||
|  |  | ||||||
|  | 	/*! Last MGCP response (in case re-transmission is required) */ | ||||||
|  | 	char *last_response; | ||||||
|  |  | ||||||
|  | 	/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */ | ||||||
|  | 	uint32_t x_osmo_ign; | ||||||
|  |  | ||||||
|  | 	/* E1 specific */ | ||||||
|  | 	struct { | ||||||
|  | 		struct osmo_i460_schan_desc scd; | ||||||
|  | 		struct osmo_i460_subchan *schan; | ||||||
|  | 		struct osmo_fsm_inst *trau_sync_fi; | ||||||
|  | 		struct osmo_trau2rtp_state *trau_rtp_st; | ||||||
|  | 		uint8_t last_amr_ft; | ||||||
|  | 		struct mgcp_rtp_codec *last_codec; | ||||||
|  | 	} e1; | ||||||
|  |  | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct mgcp_endpoint *mgcp_endp_alloc(struct mgcp_trunk *trunk, unsigned int index); | ||||||
|  | void mgcp_endp_release(struct mgcp_endpoint *endp); | ||||||
|  | int mgcp_endp_claim(struct mgcp_endpoint *endp, const char *callid); | ||||||
|  | void mgcp_endp_update(struct mgcp_endpoint *endp); | ||||||
|  | bool mgcp_endp_is_wildcarded(const char *epname); | ||||||
|  | struct mgcp_endpoint *mgcp_endp_by_name_trunk(int *cause, const char *epname, | ||||||
|  | 					      const struct mgcp_trunk *trunk); | ||||||
|  | struct mgcp_endpoint *mgcp_endp_by_name(int *cause, const char *epname, | ||||||
|  | 					struct mgcp_config *cfg); | ||||||
|  | bool mgcp_endp_avail(struct mgcp_endpoint *endp); | ||||||
|  | void mgcp_endp_add_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn); | ||||||
|  | void mgcp_endp_remove_conn(struct mgcp_endpoint *endp, struct mgcp_conn *conn); | ||||||
|  | void mgcp_endp_strip_name(char *epname_stripped, const char *epname, | ||||||
|  | 			 const struct mgcp_trunk *trunk); | ||||||
|  | struct mgcp_endpoint *mgcp_endp_find_specific(const char *epname, | ||||||
|  | 			const struct mgcp_trunk *trunk); | ||||||
							
								
								
									
										58
									
								
								include/osmocom/mgcp/mgcp_msg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								include/osmocom/mgcp/mgcp_msg.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | /* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ | ||||||
|  | /* Message parser/generator utilities */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||||
|  |  * (C) 2009-2012 by On-Waves | ||||||
|  |  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  |  | ||||||
|  | struct mgcp_conn; | ||||||
|  | struct mgcp_parse_data; | ||||||
|  | struct mgcp_endpoint; | ||||||
|  | struct mgcp_trunk; | ||||||
|  |  | ||||||
|  | void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble); | ||||||
|  |  | ||||||
|  | int mgcp_parse_conn_mode(const char *msg, struct mgcp_endpoint *endp, | ||||||
|  | 			 struct mgcp_conn *conn); | ||||||
|  |  | ||||||
|  | int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data); | ||||||
|  |  | ||||||
|  | int mgcp_parse_osmux_cid(const char *line); | ||||||
|  |  | ||||||
|  | bool mgcp_check_param(const struct mgcp_endpoint *endp, struct mgcp_trunk *trunk, const char *line); | ||||||
|  |  | ||||||
|  | int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid); | ||||||
|  |  | ||||||
|  | int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id); | ||||||
|  |  | ||||||
|  | char *mgcp_strline(char *str, char **saveptr); | ||||||
|  |  | ||||||
|  | #define for_each_line(line, save)\ | ||||||
|  | 	for (line = mgcp_strline(NULL, &save); line;\ | ||||||
|  | 	     line = mgcp_strline(NULL, &save)) | ||||||
|  |  | ||||||
|  | #define for_each_non_empty_line(line, save)\ | ||||||
|  | 	for (line = strtok_r(NULL, "\r\n", &save); line;\ | ||||||
|  | 	     line = strtok_r(NULL, "\r\n", &save)) | ||||||
							
								
								
									
										179
									
								
								include/osmocom/mgcp/mgcp_network.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								include/osmocom/mgcp/mgcp_network.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <inttypes.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/core/socket.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/mgcp/mgcp.h> | ||||||
|  |  | ||||||
|  | /* The following constant defines an RTP dummy payload that is used for | ||||||
|  |  * "UDP Hole Punching" (NAT) */ | ||||||
|  | static const char rtp_dummy_payload[] = { 0x23 }; | ||||||
|  |  | ||||||
|  | /* Check if the data in a given message buffer matches the rtp dummy payload | ||||||
|  |  * defined above */ | ||||||
|  | #define mgcp_is_rtp_dummy_payload(msg) \ | ||||||
|  | 	(msgb_length(msg) == sizeof(rtp_dummy_payload) && \ | ||||||
|  | 	memcmp(msgb_data(msg), rtp_dummy_payload, sizeof(rtp_dummy_payload)) == 0) | ||||||
|  |  | ||||||
|  | #define RTP_BUF_SIZE	4096 | ||||||
|  |  | ||||||
|  | struct mgcp_rtp_stream_state { | ||||||
|  | 	uint32_t ssrc; | ||||||
|  | 	uint16_t last_seq; | ||||||
|  | 	uint32_t last_timestamp; | ||||||
|  | 	struct rate_ctr *err_ts_ctr; | ||||||
|  | 	int32_t last_tsdelta; | ||||||
|  | 	uint32_t last_arrival_time; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct mgcp_rtp_state { | ||||||
|  | 	/* has this state structure been initialized? */ | ||||||
|  | 	int initialized; | ||||||
|  |  | ||||||
|  | 	struct { | ||||||
|  | 		/* are we patching the SSRC value? */ | ||||||
|  | 		bool patch_ssrc; | ||||||
|  | 		/* original SSRC (to which we shall patch any different SSRC) */ | ||||||
|  | 		uint32_t orig_ssrc; | ||||||
|  | 		/* offset to apply on the sequence number */ | ||||||
|  | 		int seq_offset; | ||||||
|  | 		/* offset to apply on the timestamp number */ | ||||||
|  | 		int32_t timestamp_offset; | ||||||
|  | 	} patch; | ||||||
|  |  | ||||||
|  | 	/* duration of a packet (FIXME: in which unit?) */ | ||||||
|  | 	uint32_t packet_duration; | ||||||
|  |  | ||||||
|  | 	/* Note: These states are not continuously updated, they serve as an | ||||||
|  | 	 * information source to patch certain values in the RTP header. Do | ||||||
|  | 	 * not use this state if constantly updated data about the RTP stream | ||||||
|  | 	 * is needed. (see also mgcp_patch_and_count() */ | ||||||
|  | 	struct mgcp_rtp_stream_state in_stream; | ||||||
|  | 	struct mgcp_rtp_stream_state out_stream; | ||||||
|  |  | ||||||
|  | 	/* jitter and packet loss calculation */ | ||||||
|  | 	struct { | ||||||
|  | 		int initialized; | ||||||
|  | 		uint16_t base_seq; | ||||||
|  | 		uint16_t max_seq; | ||||||
|  | 		uint32_t ssrc; | ||||||
|  | 		uint32_t jitter; | ||||||
|  | 		int32_t transit; | ||||||
|  | 		int cycles; | ||||||
|  | 	} stats; | ||||||
|  |  | ||||||
|  | 	/* Alternative values for RTP tx, in case no sufficient header | ||||||
|  | 	 * information is available so the header needs to be generated | ||||||
|  | 	 * locally (when just forwarding packets, the header of incoming | ||||||
|  | 	 * data is just re-used) */ | ||||||
|  | 	uint16_t alt_rtp_tx_sequence; | ||||||
|  | 	uint32_t alt_rtp_tx_ssrc; | ||||||
|  |  | ||||||
|  | 	bool patched_first_rtp_payload; /* FIXME: drop this, see OS#2459 */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct mgcp_rtp_codec { | ||||||
|  | 	uint32_t rate; | ||||||
|  | 	int channels; | ||||||
|  | 	uint32_t frame_duration_num; | ||||||
|  | 	uint32_t frame_duration_den; | ||||||
|  |  | ||||||
|  | 	int payload_type; | ||||||
|  | 	char audio_name[64]; | ||||||
|  | 	char subtype_name[64]; | ||||||
|  |  | ||||||
|  | 	bool param_present; | ||||||
|  | 	struct mgcp_codec_param param; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */ | ||||||
|  | struct mgcp_rtp_end { | ||||||
|  | 	/* remote IP address of the RTP socket */ | ||||||
|  | 	struct osmo_sockaddr addr; | ||||||
|  |  | ||||||
|  | 	/* in network byte order */ | ||||||
|  | 	int rtp_port, rtcp_port; | ||||||
|  |  | ||||||
|  | 	/* currently selected audio codec */ | ||||||
|  | 	struct mgcp_rtp_codec *codec; | ||||||
|  |  | ||||||
|  | 	/* array with assigned audio codecs to choose from (SDP) */ | ||||||
|  | 	struct mgcp_rtp_codec codecs[MGCP_MAX_CODECS]; | ||||||
|  |  | ||||||
|  | 	/* number of assigned audio codecs (SDP) */ | ||||||
|  | 	unsigned int codecs_assigned; | ||||||
|  |  | ||||||
|  | 	/* per endpoint data */ | ||||||
|  | 	int  frames_per_packet; | ||||||
|  | 	uint32_t packet_duration_ms; | ||||||
|  | 	int maximum_packet_time; /* -1: not set */ | ||||||
|  | 	char *fmtp_extra; | ||||||
|  | 	/* are we transmitting packets (1) or dropping (0) outbound packets */ | ||||||
|  | 	int output_enabled; | ||||||
|  | 	/* FIXME: This parameter can be set + printed, but is nowhere used! */ | ||||||
|  | 	int force_output_ptime; | ||||||
|  |  | ||||||
|  | 	/* RTP patching */ | ||||||
|  | 	int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ | ||||||
|  | 	/* should we perform align_rtp_timestamp_offset() (1) or not (0) */ | ||||||
|  | 	int force_aligned_timing; | ||||||
|  | 	bool rfc5993_hr_convert; | ||||||
|  |  | ||||||
|  | 	/* Each end has a separate socket for RTP and RTCP */ | ||||||
|  | 	struct osmo_fd rtp; | ||||||
|  | 	struct osmo_fd rtcp; | ||||||
|  |  | ||||||
|  | 	/* local UDP port number of the RTP socket; RTCP is +1 */ | ||||||
|  | 	int local_port; | ||||||
|  | 	/* where the endpoint RTP connection binds to, set during CRCX and | ||||||
|  | 	 * possibly updated during MDCX */ | ||||||
|  | 	char local_addr[INET6_ADDRSTRLEN]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | bool mgcp_rtp_end_remote_addr_available(const struct mgcp_rtp_end *rtp_end); | ||||||
|  |  | ||||||
|  | struct mgcp_rtp_tap { | ||||||
|  | 	/* is this tap active (1) or not (0) */ | ||||||
|  | 	int enabled; | ||||||
|  | 	/* IP/port to which we're forwarding the tapped data */ | ||||||
|  | 	struct osmo_sockaddr forward; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct mgcp_conn; | ||||||
|  |  | ||||||
|  | int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct osmo_sockaddr *addr, | ||||||
|  | 	      struct msgb *msg, struct mgcp_conn_rtp *conn_src, | ||||||
|  | 	      struct mgcp_conn_rtp *conn_dst); | ||||||
|  | int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn); | ||||||
|  | int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg); | ||||||
|  | void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn); | ||||||
|  | int mgcp_dispatch_e1_bridge_cb(struct msgb *msg); | ||||||
|  | void mgcp_cleanup_e1_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn); | ||||||
|  | int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port, | ||||||
|  | 			   struct mgcp_conn_rtp *conn); | ||||||
|  | void mgcp_free_rtp_port(struct mgcp_rtp_end *end); | ||||||
|  | void mgcp_patch_and_count(const struct mgcp_endpoint *endp, | ||||||
|  | 			  struct mgcp_rtp_state *state, | ||||||
|  | 			  struct mgcp_rtp_end *rtp_end, | ||||||
|  | 			  struct osmo_sockaddr *addr, struct msgb *msg); | ||||||
|  | void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn); | ||||||
|  |  | ||||||
|  | /* payload processing default functions */ | ||||||
|  | int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, | ||||||
|  | 				char *data, int *len, int buf_size); | ||||||
|  |  | ||||||
|  | int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, | ||||||
|  | 				      struct mgcp_conn_rtp *conn_dst, | ||||||
|  | 				      struct mgcp_conn_rtp *conn_src); | ||||||
|  |  | ||||||
|  | void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, | ||||||
|  | 					  const struct mgcp_rtp_codec **codec, | ||||||
|  | 					  const char **fmtp_extra, | ||||||
|  | 					  struct mgcp_conn_rtp *conn); | ||||||
|  |  | ||||||
|  | /* internal RTP Annex A counting */ | ||||||
|  | void mgcp_rtp_annex_count(const struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, | ||||||
|  | 			const uint16_t seq, const int32_t transit, | ||||||
|  | 			const uint32_t ssrc, const bool marker_bit); | ||||||
							
								
								
									
										30
									
								
								include/osmocom/mgcp/mgcp_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								include/osmocom/mgcp/mgcp_protocol.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | /* Internal structure while parsing a request */ | ||||||
|  | struct mgcp_parse_data { | ||||||
|  | 	struct mgcp_config *cfg; | ||||||
|  | 	char *epname; | ||||||
|  | 	char *trans; | ||||||
|  | 	char *save; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Local connection options */ | ||||||
|  | struct mgcp_lco { | ||||||
|  | 	char *string; | ||||||
|  | 	char *codec; | ||||||
|  | 	int pkt_period_min; /* time in ms */ | ||||||
|  | 	int pkt_period_max; /* time in ms */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | char *mgcp_debug_get_last_endpoint_name(void); | ||||||
|  |  | ||||||
|  | char *get_lco_identifier(const char *options); | ||||||
|  | int check_local_cx_options(void *ctx, const char *options); | ||||||
|  |  | ||||||
|  | struct mgcp_rtp_end; | ||||||
|  | struct mgcp_endpoint; | ||||||
|  | void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, | ||||||
|  | 			 struct mgcp_rtp_end *rtp); | ||||||
|  |  | ||||||
|  | uint32_t mgcp_rtp_packet_duration(const struct mgcp_endpoint *endp, | ||||||
|  | 				  const struct mgcp_rtp_end *rtp); | ||||||
							
								
								
									
										107
									
								
								include/osmocom/mgcp/mgcp_ratectr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								include/osmocom/mgcp/mgcp_ratectr.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | /* Global MCGP general rate counters */ | ||||||
|  | enum { | ||||||
|  | 	MGCP_GENERAL_RX_MSGS_TOTAL, | ||||||
|  | 	MGCP_GENERAL_RX_MSGS_RETRANSMITTED, | ||||||
|  | 	MGCP_GENERAL_RX_MSGS_HANDLED, | ||||||
|  | 	MGCP_GENERAL_RX_MSGS_UNHANDLED, | ||||||
|  | 	MGCP_GENERAL_RX_FAIL_MSG_PARSE, | ||||||
|  | 	MGCP_GENERAL_RX_FAIL_NO_ENDPOINT, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Trunk-global MCGP CRCX related rate counters */ | ||||||
|  | enum { | ||||||
|  | 	MGCP_CRCX_SUCCESS, | ||||||
|  | 	MGCP_CRCX_FAIL_BAD_ACTION, | ||||||
|  | 	MGCP_CRCX_FAIL_UNHANDLED_PARAM, | ||||||
|  | 	MGCP_CRCX_FAIL_MISSING_CALLID, | ||||||
|  | 	MGCP_CRCX_FAIL_INVALID_MODE, | ||||||
|  | 	MGCP_CRCX_FAIL_LIMIT_EXCEEDED, | ||||||
|  | 	MGCP_CRCX_FAIL_UNKNOWN_CALLID, | ||||||
|  | 	MGCP_CRCX_FAIL_ALLOC_CONN, | ||||||
|  | 	MGCP_CRCX_FAIL_NO_REMOTE_CONN_DESC, | ||||||
|  | 	MGCP_CRCX_FAIL_START_RTP, | ||||||
|  | 	MGCP_CRCX_FAIL_NO_OSMUX, | ||||||
|  | 	MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS, | ||||||
|  | 	MGCP_CRCX_FAIL_CODEC_NEGOTIATION, | ||||||
|  | 	MGCP_CRCX_FAIL_BIND_PORT, | ||||||
|  | 	MGCP_CRCX_FAIL_AVAIL, | ||||||
|  | 	MGCP_CRCX_FAIL_CLAIM, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Trunk-global MCGP MDCX related rate counters */ | ||||||
|  | enum { | ||||||
|  | 	MGCP_MDCX_SUCCESS, | ||||||
|  | 	MGCP_MDCX_FAIL_WILDCARD, | ||||||
|  | 	MGCP_MDCX_FAIL_NO_CONN, | ||||||
|  | 	MGCP_MDCX_FAIL_INVALID_CALLID, | ||||||
|  | 	MGCP_MDCX_FAIL_INVALID_CONNID, | ||||||
|  | 	MGCP_MDCX_FAIL_UNHANDLED_PARAM, | ||||||
|  | 	MGCP_MDCX_FAIL_NO_CONNID, | ||||||
|  | 	MGCP_MDCX_FAIL_CONN_NOT_FOUND, | ||||||
|  | 	MGCP_MDCX_FAIL_INVALID_MODE, | ||||||
|  | 	MGCP_MDCX_FAIL_INVALID_CONN_OPTIONS, | ||||||
|  | 	MGCP_MDCX_FAIL_NO_REMOTE_CONN_DESC, | ||||||
|  | 	MGCP_MDCX_FAIL_START_RTP, | ||||||
|  | 	MGCP_MDCX_FAIL_AVAIL, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Trunk-global MCGP DLCX related rate counters */ | ||||||
|  | enum { | ||||||
|  | 	MGCP_DLCX_SUCCESS, | ||||||
|  | 	MGCP_DLCX_FAIL_NO_CONN, | ||||||
|  | 	MGCP_DLCX_FAIL_INVALID_CALLID, | ||||||
|  | 	MGCP_DLCX_FAIL_INVALID_CONNID, | ||||||
|  | 	MGCP_DLCX_FAIL_UNHANDLED_PARAM, | ||||||
|  | 	MGCP_DLCX_FAIL_AVAIL, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Trunk-global E1 related counters */ | ||||||
|  | enum { | ||||||
|  |         E1_I460_TRAU_RX_FAIL_CTR, | ||||||
|  |         E1_I460_TRAU_TX_FAIL_CTR, | ||||||
|  |         E1_I460_TRAU_MUX_EMPTY_CTR, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* NOTE: When adding counters, also the dump_ratectr_* routines in vty.c must be updated. */ | ||||||
|  |  | ||||||
|  | struct mgcp_ratectr_global { | ||||||
|  | 	/* Rate counter group which contains stats for generic MGCP events. */ | ||||||
|  | 	struct rate_ctr_group *mgcp_general_ctr_group; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct mgcp_ratectr_trunk { | ||||||
|  | 	/* Rate counter group which contains stats for processed CRCX commands. */ | ||||||
|  | 	struct rate_ctr_group *mgcp_crcx_ctr_group; | ||||||
|  | 	/* Rate counter group which contains stats for processed MDCX commands. */ | ||||||
|  | 	struct rate_ctr_group *mgcp_mdcx_ctr_group; | ||||||
|  | 	/* Rate counter group which contains stats for processed DLCX commands. */ | ||||||
|  | 	struct rate_ctr_group *mgcp_dlcx_ctr_group; | ||||||
|  | 	/* Rate counter group which aggregates stats of individual RTP connections. */ | ||||||
|  | 	struct rate_ctr_group *all_rtp_conn_stats; | ||||||
|  | 	/* Rate counter group which contains stats for E1 events (only valid for E1 trunks) */ | ||||||
|  | 	struct rate_ctr_group *e1_stats; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct mgcp_config; | ||||||
|  | struct mgcp_trunk; | ||||||
|  |  | ||||||
|  | int mgcp_ratectr_global_alloc(struct mgcp_config *cfg); | ||||||
|  | void mgcp_ratectr_global_free(struct mgcp_config *cfg); | ||||||
|  | int mgcp_ratectr_trunk_alloc(struct mgcp_trunk *trunk); | ||||||
|  | void mgcp_ratectr_trunk_free(struct mgcp_trunk *trunk); | ||||||
|  |  | ||||||
|  | /* Trunk-global common stat items */ | ||||||
|  | enum { | ||||||
|  | 	TRUNK_STAT_ENDPOINTS_TOTAL, | ||||||
|  | 	TRUNK_STAT_ENDPOINTS_USED, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct mgcp_stat_trunk { | ||||||
|  | 	/* Stat item group which contains general status values of the trunk. */ | ||||||
|  | 	struct osmo_stat_item_group *common; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | int mgcp_stat_trunk_alloc(struct mgcp_trunk *trunk); | ||||||
|  | void mgcp_stat_trunk_free(struct mgcp_trunk *trunk); | ||||||
							
								
								
									
										31
									
								
								include/osmocom/mgcp/mgcp_sdp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								include/osmocom/mgcp/mgcp_sdp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | /* | ||||||
|  |  * SDP generation and parsing | ||||||
|  |  * | ||||||
|  |  * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||||
|  |  * (C) 2009-2014 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 Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp, | ||||||
|  | 			struct mgcp_conn_rtp *conn, | ||||||
|  | 			struct mgcp_parse_data *p); | ||||||
|  |  | ||||||
|  | int mgcp_write_response_sdp(const struct mgcp_endpoint *endp, | ||||||
|  | 			    const struct mgcp_conn_rtp *conn, struct msgb *sdp, | ||||||
|  | 			    const char *addr); | ||||||
							
								
								
									
										35
									
								
								include/osmocom/mgcp/mgcp_stat.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								include/osmocom/mgcp/mgcp_stat.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | /* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ | ||||||
|  | /* The statistics generator */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||||
|  |  * (C) 2009-2012 by On-Waves | ||||||
|  |  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <inttypes.h> | ||||||
|  |  | ||||||
|  | void mgcp_format_stats(char *str, size_t str_len,  struct mgcp_conn *conn); | ||||||
|  |  | ||||||
|  | /* Exposed for test purposes only, do not use actively */ | ||||||
|  | void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss); | ||||||
|  |  | ||||||
|  | /* Exposed for test purposes only, do not use actively */ | ||||||
|  | uint32_t calc_jitter(struct mgcp_rtp_state *); | ||||||
							
								
								
									
										52
									
								
								include/osmocom/mgcp/mgcp_threads_queue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								include/osmocom/mgcp/mgcp_threads_queue.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | |||||||
|  | /* | ||||||
|  |  * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * Author: Eric Wild | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stdatomic.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  |  | ||||||
|  | struct spsc { | ||||||
|  | 	atomic_uint readptr; | ||||||
|  | 	atomic_uint writeptr; | ||||||
|  |  | ||||||
|  | 	int efd_r, efd_w; /* eventfds used to block/notify readers/writers */ | ||||||
|  |  | ||||||
|  | 	int count; | ||||||
|  | 	int size_per_buf; | ||||||
|  |  | ||||||
|  | 	void *buf; /* buffer size count*size_per_buf */ | ||||||
|  | 	uintptr_t data[0]; /* count sized array of pointers to size_per_buf chunks in buf array*/ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct qchan { | ||||||
|  | 	struct spsc *a; | ||||||
|  | 	struct spsc *b; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | bool spsc_push(struct spsc *q, void *elem); | ||||||
|  | bool spsc_pop(struct spsc *q, void *elem); | ||||||
|  | ssize_t spsc_prep_pop(struct spsc *q); | ||||||
|  | int spsc_get_a_rdfd(struct qchan *q); | ||||||
|  |  | ||||||
|  | struct qchan spsc_chan_init(void *talloc_ctx, unsigned int count, unsigned int size_per_buf); | ||||||
|  | struct qchan spsc_chan_init_ex(void *talloc_ctx, unsigned int count, unsigned int size_per_buf, bool blockr_a, | ||||||
|  | 			       bool blockw_a, bool blockr_b, bool blockw_b); | ||||||
|  | void spsc_chan_close(struct qchan *q); | ||||||
							
								
								
									
										88
									
								
								include/osmocom/mgcp/mgcp_trunk.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								include/osmocom/mgcp/mgcp_trunk.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <osmocom/gsm/i460_mux.h> | ||||||
|  | #include <osmocom/abis/e1_input.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_ratectr.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define LOGPTRUNK(trunk, cat, level, fmt, args...) \ | ||||||
|  | LOGP(cat, level, "trunk:%u " fmt, \ | ||||||
|  |      trunk ? trunk->trunk_nr : 0, \ | ||||||
|  |      ## args) | ||||||
|  |  | ||||||
|  | enum mgcp_trunk_type { | ||||||
|  | 	MGCP_TRUNK_VIRTUAL, | ||||||
|  | 	MGCP_TRUNK_E1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | extern const struct value_string mgcp_trunk_type_strs[]; | ||||||
|  | static inline const char *mgcp_trunk_type_strs_str(enum mgcp_trunk_type val) | ||||||
|  | { return get_value_string(mgcp_trunk_type_strs, val); } | ||||||
|  |  | ||||||
|  | struct mgcp_trunk { | ||||||
|  | 	struct llist_head entry; | ||||||
|  |  | ||||||
|  | 	struct mgcp_config *cfg; | ||||||
|  |  | ||||||
|  | 	unsigned int trunk_nr; | ||||||
|  | 	enum mgcp_trunk_type trunk_type; | ||||||
|  |  | ||||||
|  | 	char *audio_fmtp_extra; | ||||||
|  | 	int audio_send_ptime; | ||||||
|  | 	int audio_send_name; | ||||||
|  |  | ||||||
|  | 	int no_audio_transcoding; | ||||||
|  |  | ||||||
|  | 	int omit_rtcp; | ||||||
|  | 	int keepalive_interval; | ||||||
|  |  | ||||||
|  | 	/* RTP patching */ | ||||||
|  | 	int force_constant_ssrc; /* 0: don't, 1: once */ | ||||||
|  | 	int force_aligned_timing; | ||||||
|  | 	bool rfc5993_hr_convert; | ||||||
|  |  | ||||||
|  | 	/* spec handling */ | ||||||
|  | 	int force_realloc; | ||||||
|  |  | ||||||
|  | 	/* timer */ | ||||||
|  | 	struct osmo_timer_list keepalive_timer; | ||||||
|  |  | ||||||
|  | 	/* When set, incoming RTP packets are not filtered | ||||||
|  | 	 * when ports and ip-address do not match (debug) */ | ||||||
|  | 	int rtp_accept_all; | ||||||
|  |  | ||||||
|  | 	unsigned int number_endpoints; | ||||||
|  | 	struct mgcp_endpoint **endpoints; | ||||||
|  |  | ||||||
|  | 	/* rate counters and stat items to measure the trunks overall performance and health */ | ||||||
|  | 	struct mgcp_ratectr_trunk ratectr; | ||||||
|  | 	struct mgcp_stat_trunk stats; | ||||||
|  |  | ||||||
|  | 	union { | ||||||
|  | 		/* Virtual trunk specific */ | ||||||
|  | 		struct { | ||||||
|  | 			unsigned int vty_number_endpoints; | ||||||
|  | 		} v; | ||||||
|  | 		/* E1 specific */ | ||||||
|  | 		struct { | ||||||
|  | 			unsigned int vty_line_nr; | ||||||
|  | 			bool ts_in_use[NUM_E1_TS-1]; | ||||||
|  | 			struct osmo_i460_timeslot i460_ts[NUM_E1_TS-1]; | ||||||
|  | 			/* Note: on an E1 line TS 0 is devoted to framing and | ||||||
|  | 			 * alignment and therefore only NUM_E1_TS-1 timeslots | ||||||
|  | 			 * are available for traffic. */ | ||||||
|  | 		} e1; | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct mgcp_trunk *mgcp_trunk_alloc(struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr); | ||||||
|  | int mgcp_trunk_equip(struct mgcp_trunk *trunk); | ||||||
|  | struct mgcp_trunk *mgcp_trunk_by_num(const struct mgcp_config *cfg, enum mgcp_trunk_type ttype, unsigned int nr); | ||||||
|  | struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char *epname); | ||||||
|  | int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname); | ||||||
|  | struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int num); | ||||||
|  |  | ||||||
|  | /* The virtual trunk is always created on trunk id 0 for historical reasons, | ||||||
|  |  * use this define constant as ID when allocating a virtual trunk. Other | ||||||
|  |  * trunks may be assigned with arbritrary id numbers */ | ||||||
|  | #define MGCP_VIRT_TRUNK_ID 0 | ||||||
							
								
								
									
										40
									
								
								include/osmocom/mgcp/osmux.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								include/osmocom/mgcp/osmux.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <osmocom/core/socket.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/netif/osmux.h> | ||||||
|  | struct mgcp_conn_rtp; | ||||||
|  |  | ||||||
|  | #define OSMUX_PORT	1984 | ||||||
|  |  | ||||||
|  | enum { | ||||||
|  | 	OSMUX_ROLE_BSC = 0, | ||||||
|  | 	OSMUX_ROLE_BSC_NAT, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | int osmux_init(int role, struct mgcp_config *cfg); | ||||||
|  | int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn, | ||||||
|  | 		      struct osmo_sockaddr *addr, uint16_t port); | ||||||
|  | void conn_osmux_disable(struct mgcp_conn_rtp *conn); | ||||||
|  | int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid); | ||||||
|  | void conn_osmux_release_cid(struct mgcp_conn_rtp *conn); | ||||||
|  | int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn); | ||||||
|  | int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn); | ||||||
|  |  | ||||||
|  | void osmux_cid_pool_get(uint8_t osmux_cid); | ||||||
|  | int osmux_cid_pool_get_next(void); | ||||||
|  | void osmux_cid_pool_put(uint8_t osmux_cid); | ||||||
|  | bool osmux_cid_pool_allocated(uint8_t osmux_cid); | ||||||
|  | int osmux_cid_pool_count_used(void); | ||||||
|  |  | ||||||
|  | enum osmux_state { | ||||||
|  | 	OSMUX_STATE_DISABLED = 0, /* Osmux not being currently used by endp */ | ||||||
|  | 	OSMUX_STATE_ACTIVATING,   /* Osmux was accepted in MGCP CRCX ACK. It can now be enabled by \ref osmux_enable_endpoint. */ | ||||||
|  | 	OSMUX_STATE_ENABLED,	  /* Osmux was initialized by \ref osmux_enable_endpoint and can process frames */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum osmux_usage { | ||||||
|  | 	OSMUX_USAGE_OFF = 0, | ||||||
|  | 	OSMUX_USAGE_ON = 1, | ||||||
|  | 	OSMUX_USAGE_ONLY = 2, | ||||||
|  | }; | ||||||
							
								
								
									
										12
									
								
								include/osmocom/mgcp/vty.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								include/osmocom/mgcp/vty.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <osmocom/vty/command.h> | ||||||
|  |  | ||||||
|  | enum mgcp_vty_node { | ||||||
|  | 	MGCP_NODE = _LAST_OSMOVTY_NODE + 1, | ||||||
|  | 	TRUNK_NODE, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum mgw_vty_cmd_attr { | ||||||
|  | 	MGW_CMD_ATTR_NEWCONN = 0, | ||||||
|  | }; | ||||||
| @@ -1,3 +1,14 @@ | |||||||
|  | BUILT_SOURCES = \ | ||||||
|  | 	mgcp_common.h \ | ||||||
|  | 	$(NULL) | ||||||
|  |  | ||||||
| noinst_HEADERS = \ | noinst_HEADERS = \ | ||||||
| 	mgcp_client_internal.h \ | 	mgcp_client_internal.h \ | ||||||
|  | 	mgcp_client_pool_internal.h \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
|  | mgcp_common.h: $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h | ||||||
|  | 	echo -e "/*\n\n   DO NOT EDIT THIS FILE!\n   THIS IS OVERWRITTEN DURING BUILD\n   This is an automatic copy of <osmocom/mgcp/mgcp_common.h>\n\n */" > mgcp_common.h | ||||||
|  | 	cat $(top_srcdir)/include/osmocom/mgcp/mgcp_common.h >> mgcp_common.h | ||||||
|  |  | ||||||
|  | CLEANFILES = mgcp_common.h | ||||||
|   | |||||||
| @@ -1,12 +1,18 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  | #include <arpa/inet.h> | ||||||
|  |  | ||||||
| #define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0" | #include <osmocom/mgcp_client/mgcp_common.h> | ||||||
| #define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0 |  | ||||||
|  | /* See also: RFC 3435, chapter 3.5 Transmission over UDP */ | ||||||
|  | #define MGCP_CLIENT_LOCAL_ADDR_DEFAULT NULL /* INADDR(6)_ANY */ | ||||||
|  | #define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727 | ||||||
| #define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1" | #define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1" | ||||||
| #define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427 | #define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427 | ||||||
|  |  | ||||||
|  | #define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n" | ||||||
|  |  | ||||||
| struct msgb; | struct msgb; | ||||||
| struct vty; | struct vty; | ||||||
| struct mgcp_client; | struct mgcp_client; | ||||||
| @@ -16,23 +22,113 @@ struct mgcp_client_conf { | |||||||
| 	int local_port; | 	int local_port; | ||||||
| 	const char *remote_addr; | 	const char *remote_addr; | ||||||
| 	int remote_port; | 	int remote_port; | ||||||
| 	uint16_t first_endpoint; |  | ||||||
| 	uint16_t last_endpoint; | 	/* By default, we are always addressing the MGW with e.g. 'rtpbridge/123@mgw'. | ||||||
| 	uint16_t bts_base; | 	 * If this is nonempty, the contained name will be used instead of 'mgw'. */ | ||||||
|  | 	char endpoint_domain_name[MGCP_ENDPOINT_MAXLEN]; | ||||||
|  |  | ||||||
|  | 	/* The user may configure certain endpoint names that are reset via DLCX | ||||||
|  | 	 * on startup. Usually this will be one wildcarded endpoint e.g. | ||||||
|  | 	 * 'rtpbridge/(wildcard)' or a number of specific E1 like e.g. | ||||||
|  | 	 * 'ds/e1-0/s-3/su16-4' */ | ||||||
|  | 	struct llist_head reset_epnames; | ||||||
|  |  | ||||||
|  | 	/* human readable name / description */ | ||||||
|  | 	char *description; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| typedef unsigned int mgcp_trans_id_t; | typedef unsigned int mgcp_trans_id_t; | ||||||
|  |  | ||||||
|  | /*! Enumeration of the codec types that mgcp_client is able to handle. */ | ||||||
|  | enum mgcp_codecs { | ||||||
|  | 	CODEC_PCMU_8000_1 = 0, | ||||||
|  | 	CODEC_GSM_8000_1 = 3, | ||||||
|  | 	CODEC_PCMA_8000_1 = 8, | ||||||
|  | 	CODEC_G729_8000_1 = 18, | ||||||
|  | 	CODEC_GSMEFR_8000_1 = 110, | ||||||
|  | 	CODEC_GSMHR_8000_1 = 111, | ||||||
|  | 	CODEC_AMR_8000_1 = 112, | ||||||
|  | 	CODEC_AMRWB_16000_1 = 113, | ||||||
|  | }; | ||||||
|  | /* Note: when new codec types are added, the corresponding value strings | ||||||
|  |  * in mgcp_client.c (codec_table) must be updated as well. Enumerations | ||||||
|  |  * in enum mgcp_codecs must correspond to a valid payload type. However, | ||||||
|  |  * this is an internal assumption that is made to avoid lookup tables. | ||||||
|  |  * The API-User should not rely on this coincidence! */ | ||||||
|  |  | ||||||
|  | extern const struct value_string osmo_mgcpc_codec_names[]; | ||||||
|  | static inline const char *osmo_mgcpc_codec_name(enum mgcp_codecs val) | ||||||
|  | { return get_value_string(osmo_mgcpc_codec_names, val); } | ||||||
|  |  | ||||||
|  | /*! Structure to build a payload type map to allow the defiition custom payload | ||||||
|  |  *  types. */ | ||||||
|  | struct ptmap { | ||||||
|  | 	/*! codec for which a payload type number should be defined */ | ||||||
|  | 	enum mgcp_codecs codec; | ||||||
|  |  | ||||||
|  | 	/*! payload type number (96-127) */ | ||||||
|  | 	unsigned int pt; | ||||||
|  | }; | ||||||
|  |  | ||||||
| struct mgcp_response_head { | struct mgcp_response_head { | ||||||
| 	int response_code; | 	int response_code; | ||||||
| 	mgcp_trans_id_t trans_id; | 	mgcp_trans_id_t trans_id; | ||||||
|        const char *comment; | 	char comment[MGCP_COMMENT_MAXLEN]; | ||||||
|  | 	char conn_id[MGCP_CONN_ID_MAXLEN]; | ||||||
|  | 	char endpoint[MGCP_ENDPOINT_MAXLEN]; | ||||||
|  | 	bool x_osmo_osmux_use; | ||||||
|  | 	uint8_t x_osmo_osmux_cid; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct mgcp_response { | struct mgcp_response { | ||||||
| 	char *body; | 	char *body; | ||||||
| 	struct mgcp_response_head head; | 	struct mgcp_response_head head; | ||||||
| 	uint16_t audio_port; | 	uint16_t audio_port; | ||||||
|  | 	char audio_ip[INET6_ADDRSTRLEN]; | ||||||
|  | 	unsigned int ptime; | ||||||
|  | 	enum mgcp_codecs codecs[MGCP_MAX_CODECS]; | ||||||
|  | 	unsigned int codecs_len; | ||||||
|  | 	struct ptmap ptmap[MGCP_MAX_CODECS]; | ||||||
|  | 	unsigned int ptmap_len; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum mgcp_verb { | ||||||
|  | 	MGCP_VERB_CRCX, | ||||||
|  | 	MGCP_VERB_MDCX, | ||||||
|  | 	MGCP_VERB_DLCX, | ||||||
|  | 	MGCP_VERB_AUEP, | ||||||
|  | 	MGCP_VERB_RSIP, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define MGCP_MSG_PRESENCE_ENDPOINT	0x0001 | ||||||
|  | #define MGCP_MSG_PRESENCE_CALL_ID	0x0002 | ||||||
|  | #define MGCP_MSG_PRESENCE_CONN_ID	0x0004 | ||||||
|  | #define MGCP_MSG_PRESENCE_AUDIO_IP	0x0008 | ||||||
|  | #define MGCP_MSG_PRESENCE_AUDIO_PORT	0x0010 | ||||||
|  | #define MGCP_MSG_PRESENCE_CONN_MODE	0x0020 | ||||||
|  | #define MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID 0x4000 | ||||||
|  | #define MGCP_MSG_PRESENCE_X_OSMO_IGN	0x8000 | ||||||
|  |  | ||||||
|  | struct mgcp_msg { | ||||||
|  | 	enum mgcp_verb verb; | ||||||
|  | 	/* See MGCP_MSG_PRESENCE_* constants */ | ||||||
|  | 	uint32_t presence; | ||||||
|  | 	char endpoint[MGCP_ENDPOINT_MAXLEN]; | ||||||
|  | 	unsigned int call_id; | ||||||
|  | 	char *conn_id; | ||||||
|  | 	uint16_t audio_port; | ||||||
|  | 	char *audio_ip; | ||||||
|  | 	enum mgcp_connection_mode conn_mode; | ||||||
|  | 	unsigned int ptime; | ||||||
|  | 	enum mgcp_codecs codecs[MGCP_MAX_CODECS]; | ||||||
|  | 	unsigned int codecs_len; | ||||||
|  | 	struct ptmap ptmap[MGCP_MAX_CODECS]; | ||||||
|  | 	unsigned int ptmap_len; | ||||||
|  | 	uint32_t x_osmo_ign; | ||||||
|  | 	bool x_osmo_osmux_use; | ||||||
|  | 	int x_osmo_osmux_cid; /* -1 is wildcard */ | ||||||
|  | 	bool param_present; | ||||||
|  | 	struct mgcp_codec_param param; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| void mgcp_client_conf_init(struct mgcp_client_conf *conf); | void mgcp_client_conf_init(struct mgcp_client_conf *conf); | ||||||
| @@ -43,13 +139,17 @@ struct mgcp_client_conf *mgcp_client_conf_actual(struct mgcp_client *mgcp); | |||||||
| struct mgcp_client *mgcp_client_init(void *ctx, | struct mgcp_client *mgcp_client_init(void *ctx, | ||||||
| 				     struct mgcp_client_conf *conf); | 				     struct mgcp_client_conf *conf); | ||||||
| int mgcp_client_connect(struct mgcp_client *mgcp); | int mgcp_client_connect(struct mgcp_client *mgcp); | ||||||
|  | int mgcp_client_connect2(struct mgcp_client *mgcp, unsigned int retry_n_ports); | ||||||
|  | void mgcp_client_disconnect(struct mgcp_client *mgcp); | ||||||
|  |  | ||||||
| const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp); | const char *mgcp_client_remote_addr_str(struct mgcp_client *mgcp); | ||||||
| uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp); | uint16_t mgcp_client_remote_port(struct mgcp_client *mgcp); | ||||||
| uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp); | uint32_t mgcp_client_remote_addr_n(struct mgcp_client *mgcp) OSMO_DEPRECATED("deprecated, returns 0"); | ||||||
|  |  | ||||||
| int mgcp_client_next_endpoint(struct mgcp_client *client); | const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp); | ||||||
| void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client); | const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp); | ||||||
|  | const char *mgcp_client_e1_epname(void *ctx, const struct mgcp_client *mgcp, uint8_t trunk_id, uint8_t ts, | ||||||
|  | 				  uint8_t rate, uint8_t offset); | ||||||
|  |  | ||||||
| /* Invoked when an MGCP response is received or sending failed.  When the | /* Invoked when an MGCP response is received or sending failed.  When the | ||||||
|  * response is passed as NULL, this indicates failure during transmission. */ |  * response is passed as NULL, this indicates failure during transmission. */ | ||||||
| @@ -58,16 +158,23 @@ int mgcp_response_parse_params(struct mgcp_response *r); | |||||||
|  |  | ||||||
| int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg, | int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg, | ||||||
| 		   mgcp_response_cb_t response_cb, void *priv); | 		   mgcp_response_cb_t response_cb, void *priv); | ||||||
|  | int mgcp_client_cancel(struct mgcp_client *mgcp, mgcp_trans_id_t trans_id); | ||||||
|  |  | ||||||
| enum mgcp_connection_mode; | enum mgcp_connection_mode; | ||||||
|  |  | ||||||
| struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp, | struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg); | ||||||
| 			   uint16_t rtp_endpoint, unsigned int call_id, | mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg); | ||||||
| 			   enum mgcp_connection_mode mode); |  | ||||||
|  |  | ||||||
| struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp, | extern const struct value_string mgcp_client_connection_mode_strs[]; | ||||||
| 			   uint16_t rtp_endpoint, const char *rtp_conn_addr, | static inline const char *mgcp_client_cmode_name(enum mgcp_connection_mode mode) | ||||||
| 			   uint16_t rtp_port, enum mgcp_connection_mode mode); | { | ||||||
|  | 	return get_value_string(mgcp_client_connection_mode_strs, mode); | ||||||
|  | } | ||||||
|  |  | ||||||
| struct msgb *mgcp_msg_dlcx(struct mgcp_client *mgcp, uint16_t rtp_endpoint, | enum mgcp_codecs map_str_to_codec(const char *str); | ||||||
| 			   unsigned int call_id); | unsigned int map_codec_to_pt(const struct ptmap *ptmap, unsigned int ptmap_len, | ||||||
|  | 			     enum mgcp_codecs codec); | ||||||
|  | enum mgcp_codecs map_pt_to_codec(struct ptmap *ptmap, unsigned int ptmap_len, | ||||||
|  | 				 unsigned int pt); | ||||||
|  |  | ||||||
|  | const char *mgcp_client_name(const struct mgcp_client *mgcp); | ||||||
|   | |||||||
							
								
								
									
										54
									
								
								include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | /* FSM to manage multiple connections of an MGW endpoint */ | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <osmocom/mgcp_client/mgcp_client_fsm.h> | ||||||
|  |  | ||||||
|  | #define LOG_MGCPC_EP(ep, level, fmt, args...) do { \ | ||||||
|  | 	LOGPFSML(ep->fi, level, "%s " fmt, \ | ||||||
|  | 		 osmo_mgcpc_ep_name(ep), ## args); \ | ||||||
|  | 	} while(0) | ||||||
|  |  | ||||||
|  | struct osmo_mgcpc_ep; | ||||||
|  | struct osmo_mgcpc_ep_ci; | ||||||
|  | struct osmo_tdef; | ||||||
|  |  | ||||||
|  | struct osmo_mgcpc_ep *osmo_mgcpc_ep_alloc(struct osmo_fsm_inst *parent, uint32_t parent_term_event, | ||||||
|  | 					  struct mgcp_client *mgcp_client, | ||||||
|  | 					  const struct osmo_tdef *T_defs, | ||||||
|  | 					  const char *fsm_id, | ||||||
|  | 					  const char *endpoint_str_fmt, ...); | ||||||
|  |  | ||||||
|  | struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_add(struct osmo_mgcpc_ep *ep, const char *label_fmt, ...); | ||||||
|  | const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci); | ||||||
|  | bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest); | ||||||
|  | bool osmo_mgcpc_ep_ci_get_crcx_info_to_osmux_cid(const struct osmo_mgcpc_ep_ci *ci, uint8_t* cid); | ||||||
|  |  | ||||||
|  | const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_remote_rtp_info(const struct osmo_mgcpc_ep_ci *ci); | ||||||
|  |  | ||||||
|  | void osmo_mgcpc_ep_ci_request(struct osmo_mgcpc_ep_ci *ci, | ||||||
|  | 			      enum mgcp_verb verb, const struct mgcp_conn_peer *verb_info, | ||||||
|  | 			      struct osmo_fsm_inst *notify, | ||||||
|  | 			      uint32_t event_success, uint32_t event_failure, | ||||||
|  | 			      void *notify_data); | ||||||
|  |  | ||||||
|  | void osmo_mgcpc_ep_cancel_notify(struct osmo_mgcpc_ep *ep, struct osmo_fsm_inst *notify); | ||||||
|  | struct osmo_mgcpc_ep *osmo_mgcpc_ep_ci_ep(struct osmo_mgcpc_ep_ci *ci); | ||||||
|  |  | ||||||
|  | /*! Dispatch a DLCX for the given connection. | ||||||
|  |  * \param ci  Connection identifier as obtained from osmo_mgcpc_ep_ci_add(). | ||||||
|  |  */ | ||||||
|  | static inline void osmo_mgcpc_ep_ci_dlcx(struct osmo_mgcpc_ep_ci *ci) | ||||||
|  | { | ||||||
|  | 	osmo_mgcpc_ep_ci_request(ci, MGCP_VERB_DLCX, NULL, NULL, 0, 0, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep); | ||||||
|  |  | ||||||
|  | const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep); | ||||||
|  | const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci); | ||||||
|  | const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci); | ||||||
|  | struct mgcp_client *osmo_mgcpc_ep_client(const struct osmo_mgcpc_ep *ep); | ||||||
|  |  | ||||||
|  | extern const struct value_string osmo_mgcp_verb_names[]; | ||||||
|  | static inline const char *osmo_mgcp_verb_name(enum mgcp_verb val) | ||||||
|  | { return get_value_string(osmo_mgcp_verb_names, val); } | ||||||
							
								
								
									
										74
									
								
								include/osmocom/mgcp_client/mgcp_client_fsm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								include/osmocom/mgcp_client/mgcp_client_fsm.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <osmocom/mgcp_client/mgcp_common.h> | ||||||
|  | #include <osmocom/mgcp_client/mgcp_client.h> | ||||||
|  | #include <osmocom/gsm/protocol/gsm_04_08.h> | ||||||
|  |  | ||||||
|  | /*! This struct organizes the connection infromation one connection side | ||||||
|  |  *  (either remote or local). It is used to pass parameters (local) to the FSM | ||||||
|  |  *  and get responses (remote) from the FSM as pointer attached to the FSM | ||||||
|  |  *  event. | ||||||
|  |  * | ||||||
|  |  *  When modifiying a connection, the endpoint and call_id members may be left | ||||||
|  |  *  unpopulated. The call_id field is ignored in this case. If an endpoint | ||||||
|  |  *  identifier is supplied it is checked against the internal state to make | ||||||
|  |  *  sure it is correct. */ | ||||||
|  | struct mgcp_conn_peer { | ||||||
|  | 	/*! RTP connection IP-Address (optional, string e.g. "127.0.0.1") */ | ||||||
|  | 	char addr[INET6_ADDRSTRLEN]; | ||||||
|  |  | ||||||
|  | 	/*! RTP connection IP-Port (optional)  */ | ||||||
|  | 	uint16_t port; | ||||||
|  |  | ||||||
|  | 	/*! RTP endpoint */ | ||||||
|  | 	char endpoint[MGCP_ENDPOINT_MAXLEN]; | ||||||
|  |  | ||||||
|  | 	/*! CALL ID (unique per connection) */ | ||||||
|  | 	unsigned int call_id; | ||||||
|  |  | ||||||
|  | 	/*! RTP packetization interval (optional) */ | ||||||
|  | 	unsigned int ptime; | ||||||
|  |  | ||||||
|  | 	/*! RTP codec list (optional) */ | ||||||
|  | 	enum mgcp_codecs codecs[MGCP_MAX_CODECS]; | ||||||
|  |  | ||||||
|  | 	/*! Number of codecs in RTP codec list (optional) */ | ||||||
|  | 	unsigned int codecs_len; | ||||||
|  |  | ||||||
|  | 	/*! RTP payload type map (optional, only needed when payload types are | ||||||
|  | 	 * used that differ from what IANA/3GPP defines) */ | ||||||
|  | 	struct ptmap ptmap[MGCP_MAX_CODECS]; | ||||||
|  |  | ||||||
|  | 	/*! RTP payload type map length (optional, only needed when payload | ||||||
|  | 	 * types are used that differ from what IANA/3GPP defines) */ | ||||||
|  | 	unsigned int ptmap_len; | ||||||
|  |  | ||||||
|  | 	/*! If nonzero, send 'X-Osmo-IGN:' header. This is useful e.g. for SCCPlite MSCs where the MSC is | ||||||
|  | 	 * known to issue incoherent or unknown CallIDs / to issue CRCX commands with a different domain | ||||||
|  | 	 * name than the BSC. An OsmoMGW will then ignore these and not fail on mismatches. */ | ||||||
|  | 	uint32_t x_osmo_ign; | ||||||
|  |  | ||||||
|  | 	/*! send 'X-Osmux: %d' header (or "*" as wildcard). */ | ||||||
|  | 	bool x_osmo_osmux_use; | ||||||
|  | 	/*! -1 means send wildcard. */ | ||||||
|  | 	int x_osmo_osmux_cid; | ||||||
|  |  | ||||||
|  | 	/*! If left MGCP_CONN_NONE, use MGCP_CONN_RECV_ONLY or MGCP_CONN_RECV_SEND, depending on whether an audio RTP | ||||||
|  | 	 * address is set. If != MGCP_CONN_NONE, force this conn mode. */ | ||||||
|  | 	enum mgcp_connection_mode conn_mode; | ||||||
|  |  | ||||||
|  | 	/*! If the codec requires additional format parameters (fmtp), those cann be set here, see also | ||||||
|  | 	 * mgcp_common.h */ | ||||||
|  | 	bool param_present; | ||||||
|  | 	struct mgcp_codec_param param; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, uint32_t parent_term_evt, | ||||||
|  | 				       uint32_t parent_evt, struct mgcp_conn_peer *conn_peer); | ||||||
|  | int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer); | ||||||
|  | void mgcp_conn_delete(struct osmo_fsm_inst *fi); | ||||||
|  |  | ||||||
|  | const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi); | ||||||
|  | struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi); | ||||||
|  |  | ||||||
|  | const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info); | ||||||
| @@ -1,14 +1,22 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
|  | #include <osmocom/core/write_queue.h> | ||||||
|  |  | ||||||
| #define MSGB_CB_MGCP_TRANS_ID 0 | #define MSGB_CB_MGCP_TRANS_ID 0 | ||||||
|  |  | ||||||
|  | /* Struct that holds one endpoint name */ | ||||||
|  | struct reset_ep { | ||||||
|  | 	struct llist_head list; | ||||||
|  | 	char name[MGCP_ENDPOINT_MAXLEN]; | ||||||
|  | }; | ||||||
|  |  | ||||||
| struct mgcp_client { | struct mgcp_client { | ||||||
| 	struct mgcp_client_conf actual; | 	struct mgcp_client_conf actual; | ||||||
| 	uint32_t remote_addr; |  | ||||||
| 	struct osmo_wqueue wq; | 	struct osmo_wqueue wq; | ||||||
| 	mgcp_trans_id_t next_trans_id; | 	mgcp_trans_id_t next_trans_id; | ||||||
| 	struct llist_head responses_pending; | 	struct llist_head responses_pending; | ||||||
| 	struct llist_head inuse_endpoints; | 	struct llist_head inuse_endpoints; | ||||||
|  | 	struct mgcp_client_pool *pool; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct mgcp_inuse_endpoint { | struct mgcp_inuse_endpoint { | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								include/osmocom/mgcp_client/mgcp_client_pool.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								include/osmocom/mgcp_client/mgcp_client_pool.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | struct mgcp_client; | ||||||
|  | struct mgcp_client_pool; | ||||||
|  |  | ||||||
|  | struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx); | ||||||
|  | void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool); | ||||||
|  | unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool); | ||||||
|  | void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client); | ||||||
|  | struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool); | ||||||
|  | void mgcp_client_pool_put(struct mgcp_client *mgcp_client); | ||||||
							
								
								
									
										45
									
								
								include/osmocom/mgcp_client/mgcp_client_pool_internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								include/osmocom/mgcp_client/mgcp_client_pool_internal.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | /* Struct to handle a member of a pool of MGWs. */ | ||||||
|  | struct mgcp_client_pool_member { | ||||||
|  | 	struct llist_head list; | ||||||
|  |  | ||||||
|  | 	/* Reference number assinged by VTY. This number is used to manage the pool from the VTY and to identify it in | ||||||
|  | 	 * the log. */ | ||||||
|  | 	unsigned int nr; | ||||||
|  |  | ||||||
|  | 	/* MGCP client configuration, this is not the running configuration, when mgcp_client_init() is executed, a | ||||||
|  | 	 * copy of this config is created. */ | ||||||
|  | 	struct mgcp_client_conf conf; | ||||||
|  |  | ||||||
|  | 	/* MGCP client descriptor, will be automatically allocated when mgcp_client_pool_connect() is called. (the MGCP | ||||||
|  | 	 * client is connected when this pointer is populated) */ | ||||||
|  | 	struct mgcp_client *client; | ||||||
|  |  | ||||||
|  | 	/* A pool member may be set as 'blocked' from the VTY, this means that the pool member may still work and serve | ||||||
|  | 	 * ongoing calls, but it won't be picked from the pool anymore. */ | ||||||
|  | 	bool blocked; | ||||||
|  |  | ||||||
|  | 	/* Reference counter to count how often this pool member is currently picked. */ | ||||||
|  | 	unsigned int refcount; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Struct to handle a pool of MGWs. (Use _pool functions) */ | ||||||
|  | struct mgcp_client_pool { | ||||||
|  |  | ||||||
|  | 	/* A pointer to a 'single' mgcp client. This is a non-pooled MGCP client that is configured using | ||||||
|  | 	 * mgcp_client_vty_init() and actively registered by the API user using mgcp_client_pool_register_single() */ | ||||||
|  | 	struct mgcp_client *mgcp_client_single; | ||||||
|  |  | ||||||
|  | 	/* A list that manages the pool members (see above) */ | ||||||
|  | 	struct llist_head pool; | ||||||
|  |  | ||||||
|  | 	/* String to use for indentation when writing the configuration file to the VTY. This field is populated by | ||||||
|  | 	 * mgcp_client_pool_vty_init() */ | ||||||
|  | 	char *vty_indent; | ||||||
|  |  | ||||||
|  | 	/* VTY node specification used with this pool. This field is populated by mgcp_client_pool_vty_init() */ | ||||||
|  | 	struct cmd_node *vty_node; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member); | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| prefix=@prefix@ |  | ||||||
| exec_prefix=@exec_prefix@ |  | ||||||
| libdir=@libdir@ |  | ||||||
| includedir=@includedir@ |  | ||||||
|  |  | ||||||
| Name: Osmocom legacy Media Gateway Control Protocol library |  | ||||||
| Description: C Utility Library |  | ||||||
| Version: @VERSION@ |  | ||||||
| Libs: -L${libdir} -losmo-legacy-mgcp |  | ||||||
| Cflags: -I${includedir}/ |  | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| #!/usr/bin/env python | #!/usr/bin/env python3 | ||||||
|  |  | ||||||
| # (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com> | # (C) 2013 by Katerina Barone-Adesi <kat.obsc@gmail.com> | ||||||
| # This program is free software: you can redistribute it and/or modify | # This program is free software: you can redistribute it and/or modify | ||||||
| @@ -16,13 +16,13 @@ | |||||||
|  |  | ||||||
|  |  | ||||||
| app_configs = { | app_configs = { | ||||||
|     "mgcp": ["doc/examples/osmo-bsc_mgcp/mgcp.cfg"], |     "osmo-mgw": ["doc/examples/osmo-mgw/osmo-mgw.cfg"], | ||||||
| } | } | ||||||
|  |  | ||||||
| apps = [(4243, "src/osmo-bsc_mgcp/osmo-bsc_mgcp", "OpenBSC MGCP", "mgcp"), | apps = [(4243, "src/osmo-mgw/osmo-mgw", "OsmoMGW", "osmo-mgw"), | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
| vty_command = ["./src/osmo-bsc_mgcp/osmo-bsc_mgcp", "-c", | vty_command = ["./src/osmo-mgw/osmo-mgw", "-c", | ||||||
|                "doc/examples/osmo-bsc_mgcp/mgcp.cfg"] |                "doc/examples/osmo-mgw/osmo-mgw.cfg"] | ||||||
|  |  | ||||||
| vty_app = apps[0] | vty_app = apps[0] | ||||||
|   | |||||||
| @@ -21,11 +21,11 @@ AM_LDFLAGS = \ | |||||||
|  |  | ||||||
| # Libraries | # Libraries | ||||||
| SUBDIRS = \ | SUBDIRS = \ | ||||||
| 	libosmo-legacy-mgcp \ |  | ||||||
| 	libosmo-mgcp-client \ | 	libosmo-mgcp-client \ | ||||||
|  | 	libosmo-mgcp \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
| # Programs | # Programs | ||||||
| SUBDIRS += \ | SUBDIRS += \ | ||||||
| 	osmo-bsc_mgcp \ | 	osmo-mgw \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|   | |||||||
| @@ -1,51 +0,0 @@ | |||||||
| AM_CPPFLAGS = \ |  | ||||||
| 	$(all_includes) \ |  | ||||||
| 	-I$(top_srcdir)/include \ |  | ||||||
| 	-I$(top_builddir) \ |  | ||||||
| 	$(NULL) |  | ||||||
|  |  | ||||||
| AM_CFLAGS = \ |  | ||||||
| 	-Wall \ |  | ||||||
| 	$(LIBOSMOCORE_CFLAGS) \ |  | ||||||
| 	$(LIBOSMOVTY_CFLAGS) \ |  | ||||||
| 	$(LIBOSMONETIF_CFLAGS) \ |  | ||||||
| 	$(COVERAGE_CFLAGS) \ |  | ||||||
| 	$(LIBBCG729_CFLAGS) \ |  | ||||||
| 	$(NULL) |  | ||||||
|  |  | ||||||
| AM_LDFLAGS = \ |  | ||||||
| 	$(LIBOSMOCORE_LIBS) \ |  | ||||||
| 	$(LIBOSMOGSM_LIBS) \ |  | ||||||
| 	$(LIBOSMONETIF_LIBS) \ |  | ||||||
| 	$(COVERAGE_LDFLAGS) \ |  | ||||||
| 	$(LIBBCG729_LIBS) \ |  | ||||||
| 	$(NULL) |  | ||||||
|  |  | ||||||
| # This is _NOT_ the library release version, it's an API version. |  | ||||||
| # Please read Chapter 6 "Library interface versions" of the libtool |  | ||||||
| # documentation before making any modification |  | ||||||
| LEGACY_MGCP_LIBVERSION=0:0:0 |  | ||||||
|  |  | ||||||
| lib_LTLIBRARIES = \ |  | ||||||
| 	libosmo-legacy-mgcp.la \ |  | ||||||
| 	$(NULL) |  | ||||||
|  |  | ||||||
| noinst_HEADERS = \ |  | ||||||
| 	g711common.h \ |  | ||||||
| 	$(NULL) |  | ||||||
|  |  | ||||||
| libosmo_legacy_mgcp_la_SOURCES = \ |  | ||||||
| 	mgcp_common.c \ |  | ||||||
| 	mgcp_protocol.c \ |  | ||||||
| 	mgcp_network.c \ |  | ||||||
| 	mgcp_vty.c \ |  | ||||||
| 	mgcp_osmux.c \ |  | ||||||
| 	mgcp_sdp.c \ |  | ||||||
| 	$(NULL) |  | ||||||
| if BUILD_MGCP_TRANSCODING |  | ||||||
| libosmo_legacy_mgcp_la_SOURCES += \ |  | ||||||
| 	mgcp_transcode.c \ |  | ||||||
| 	$(NULL) |  | ||||||
| endif |  | ||||||
|  |  | ||||||
| libosmo_legacy_mgcp_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LEGACY_MGCP_LIBVERSION) |  | ||||||
| @@ -1,54 +0,0 @@ | |||||||
| /* Media Gateway Control Protocol Media Gateway: RFC 3435 */ |  | ||||||
| /* Implementations useful both for the MGCP GW as well as MGCP GW clients */ |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> |  | ||||||
|  * All Rights Reserved |  | ||||||
|  * |  | ||||||
|  * This program is free software; you can redistribute it and/or modify |  | ||||||
|  * it under the terms of the GNU Affero 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 Affero General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU Affero General Public License |  | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <errno.h> |  | ||||||
|  |  | ||||||
| #include <osmocom/core/utils.h> |  | ||||||
| #include <osmocom/legacy_mgcp/mgcp.h> |  | ||||||
|  |  | ||||||
| const struct value_string mgcp_connection_mode_strs[] = { |  | ||||||
| 	{ MGCP_CONN_NONE, "none" }, |  | ||||||
| 	{ MGCP_CONN_RECV_SEND, "sendrecv" }, |  | ||||||
| 	{ MGCP_CONN_SEND_ONLY, "sendonly" }, |  | ||||||
| 	{ MGCP_CONN_RECV_ONLY, "recvonly" }, |  | ||||||
| 	{ MGCP_CONN_LOOPBACK, "loopback" }, |  | ||||||
| 	{ 0, NULL } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| /* Ensure that the msg->l2h is NUL terminated. */ |  | ||||||
| int mgcp_msg_terminate_nul(struct msgb *msg) |  | ||||||
| { |  | ||||||
| 	unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */ |  | ||||||
| 	if (tail[-1] == '\0') |  | ||||||
| 		/* nothing to do */; |  | ||||||
| 	else if (msgb_tailroom(msg) > 0) |  | ||||||
| 		tail[0] = '\0'; |  | ||||||
| 	else if (tail[-1] == '\r' || tail[-1] == '\n') |  | ||||||
| 		tail[-1] = '\0'; |  | ||||||
| 	else { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " |  | ||||||
| 		     "Length: %d, Buffer size: %d\n", |  | ||||||
| 		     msgb_l2len(msg), msg->data_len); |  | ||||||
| 		return -ENOTSUP; |  | ||||||
| 	} |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,583 +0,0 @@ | |||||||
| /* |  | ||||||
|  * (C) 2012-2013 by Pablo Neira Ayuso <pablo@gnumonks.org> |  | ||||||
|  * (C) 2012-2013 by On Waves ehf <http://www.on-waves.com> |  | ||||||
|  * All rights not specifically granted under this license are reserved. |  | ||||||
|  * |  | ||||||
|  * This program is free software; you can redistribute it and/or modify it |  | ||||||
|  * under the terms of the GNU Affero General Public License as published by the |  | ||||||
|  * Free Software Foundation; either version 3 of the License, or (at your |  | ||||||
|  * option) any later version. |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <stdio.h> /* for printf */ |  | ||||||
| #include <string.h> /* for memcpy */ |  | ||||||
| #include <stdlib.h> /* for abs */ |  | ||||||
| #include <inttypes.h> /* for PRIu64 */ |  | ||||||
| #include <netinet/in.h> |  | ||||||
| #include <osmocom/core/msgb.h> |  | ||||||
| #include <osmocom/core/talloc.h> |  | ||||||
|  |  | ||||||
| #include <osmocom/netif/osmux.h> |  | ||||||
| #include <osmocom/netif/rtp.h> |  | ||||||
|  |  | ||||||
| #include <osmocom/legacy_mgcp/mgcp.h> |  | ||||||
| #include <osmocom/legacy_mgcp/mgcp_internal.h> |  | ||||||
| #include <osmocom/legacy_mgcp/osmux.h> |  | ||||||
|  |  | ||||||
| static struct osmo_fd osmux_fd; |  | ||||||
|  |  | ||||||
| static LLIST_HEAD(osmux_handle_list); |  | ||||||
|  |  | ||||||
| struct osmux_handle { |  | ||||||
| 	struct llist_head head; |  | ||||||
| 	struct osmux_in_handle *in; |  | ||||||
| 	struct in_addr rem_addr; |  | ||||||
| 	int rem_port; |  | ||||||
| 	int refcnt; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static void *osmux; |  | ||||||
|  |  | ||||||
| static void osmux_deliver(struct msgb *batch_msg, void *data) |  | ||||||
| { |  | ||||||
| 	struct osmux_handle *handle = data; |  | ||||||
| 	struct sockaddr_in out = { |  | ||||||
| 		.sin_family = AF_INET, |  | ||||||
| 		.sin_port = handle->rem_port, |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr)); |  | ||||||
| 	sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0, |  | ||||||
| 		(struct sockaddr *)&out, sizeof(out)); |  | ||||||
| 	msgb_free(batch_msg); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct osmux_handle * |  | ||||||
| osmux_handle_find_get(struct in_addr *addr, int rem_port) |  | ||||||
| { |  | ||||||
| 	struct osmux_handle *h; |  | ||||||
|  |  | ||||||
| 	/* Lookup for existing OSMUX handle for this destination address. */ |  | ||||||
| 	llist_for_each_entry(h, &osmux_handle_list, head) { |  | ||||||
| 		if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 && |  | ||||||
| 		    h->rem_port == rem_port) { |  | ||||||
| 			LOGP(DLMGCP, LOGL_DEBUG, "using existing OSMUX handle " |  | ||||||
| 						"for addr=%s:%d\n", |  | ||||||
| 				inet_ntoa(*addr), ntohs(rem_port)); |  | ||||||
| 			h->refcnt++; |  | ||||||
| 			return h; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void osmux_handle_put(struct osmux_in_handle *in) |  | ||||||
| { |  | ||||||
| 	struct osmux_handle *h; |  | ||||||
|  |  | ||||||
| 	/* Lookup for existing OSMUX handle for this destination address. */ |  | ||||||
| 	llist_for_each_entry(h, &osmux_handle_list, head) { |  | ||||||
| 		if (h->in == in) { |  | ||||||
| 			if (--h->refcnt == 0) { |  | ||||||
| 				LOGP(DLMGCP, LOGL_INFO, |  | ||||||
| 				     "Releasing unused osmux handle for %s:%d\n", |  | ||||||
| 				     inet_ntoa(h->rem_addr), |  | ||||||
| 				     ntohs(h->rem_port)); |  | ||||||
| 				LOGP(DLMGCP, LOGL_INFO, "Stats: " |  | ||||||
| 				     "input RTP msgs: %u bytes: %"PRIu64" " |  | ||||||
| 				     "output osmux msgs: %u bytes: %"PRIu64"\n", |  | ||||||
| 				     in->stats.input_rtp_msgs, |  | ||||||
| 				     in->stats.input_rtp_bytes, |  | ||||||
| 				     in->stats.output_osmux_msgs, |  | ||||||
| 				     in->stats.output_osmux_bytes); |  | ||||||
| 				llist_del(&h->head); |  | ||||||
| 				osmux_xfrm_input_fini(h->in); |  | ||||||
| 				talloc_free(h); |  | ||||||
| 			} |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	LOGP(DLMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct osmux_handle * |  | ||||||
| osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) |  | ||||||
| { |  | ||||||
| 	struct osmux_handle *h; |  | ||||||
|  |  | ||||||
| 	h = talloc_zero(osmux, struct osmux_handle); |  | ||||||
| 	if (!h) |  | ||||||
| 		return NULL; |  | ||||||
| 	h->rem_addr = *addr; |  | ||||||
| 	h->rem_port = rem_port; |  | ||||||
| 	h->refcnt++; |  | ||||||
|  |  | ||||||
| 	h->in = talloc_zero(h, struct osmux_in_handle); |  | ||||||
| 	if (!h->in) { |  | ||||||
| 		talloc_free(h); |  | ||||||
| 		return NULL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	h->in->osmux_seq = 0; /* sequence number to start OSmux message from */ |  | ||||||
| 	h->in->batch_factor = cfg->osmux_batch; |  | ||||||
| 	/* If batch size is zero, the library defaults to 1470 bytes. */ |  | ||||||
| 	h->in->batch_size = cfg->osmux_batch_size; |  | ||||||
| 	h->in->deliver = osmux_deliver; |  | ||||||
| 	osmux_xfrm_input_init(h->in); |  | ||||||
| 	h->in->data = h; |  | ||||||
|  |  | ||||||
| 	llist_add(&h->head, &osmux_handle_list); |  | ||||||
|  |  | ||||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n", |  | ||||||
| 		inet_ntoa(*addr), ntohs(rem_port)); |  | ||||||
|  |  | ||||||
| 	return h; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct osmux_in_handle * |  | ||||||
| osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) |  | ||||||
| { |  | ||||||
| 	struct osmux_handle *h; |  | ||||||
|  |  | ||||||
| 	h = osmux_handle_find_get(addr, rem_port); |  | ||||||
| 	if (h != NULL) |  | ||||||
| 		return h->in; |  | ||||||
|  |  | ||||||
| 	h = osmux_handle_alloc(cfg, addr, rem_port); |  | ||||||
| 	if (h == NULL) |  | ||||||
| 		return NULL; |  | ||||||
|  |  | ||||||
| 	return h->in; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp) |  | ||||||
| { |  | ||||||
| 	int ret; |  | ||||||
| 	struct msgb *msg; |  | ||||||
|  |  | ||||||
| 	msg = msgb_alloc(4096, "RTP"); |  | ||||||
| 	if (!msg) |  | ||||||
| 		return 0; |  | ||||||
|  |  | ||||||
| 	memcpy(msg->data, buf, rc); |  | ||||||
| 	msgb_put(msg, rc); |  | ||||||
|  |  | ||||||
| 	while ((ret = osmux_xfrm_input(endp->osmux.in, msg, endp->osmux.cid)) > 0) { |  | ||||||
| 		/* batch full, build and deliver it */ |  | ||||||
| 		osmux_xfrm_input_deliver(endp->osmux.in); |  | ||||||
| 	} |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct mgcp_endpoint * |  | ||||||
| endpoint_lookup(struct mgcp_config *cfg, int cid, |  | ||||||
| 		struct in_addr *from_addr, int type) |  | ||||||
| { |  | ||||||
| 	struct mgcp_endpoint *tmp = NULL; |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	/* Lookup for the endpoint that corresponds to this port */ |  | ||||||
| 	for (i=0; i<cfg->trunk.number_endpoints; i++) { |  | ||||||
| 		struct in_addr *this; |  | ||||||
|  |  | ||||||
| 		tmp = &cfg->trunk.endpoints[i]; |  | ||||||
|  |  | ||||||
| 		if (!tmp->allocated) |  | ||||||
| 			continue; |  | ||||||
|  |  | ||||||
| 		switch(type) { |  | ||||||
| 		case MGCP_DEST_NET: |  | ||||||
| 			this = &tmp->net_end.addr; |  | ||||||
| 			break; |  | ||||||
| 		case MGCP_DEST_BTS: |  | ||||||
| 			this = &tmp->bts_end.addr; |  | ||||||
| 			break; |  | ||||||
| 		default: |  | ||||||
| 			/* Should not ever happen */ |  | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type); |  | ||||||
| 			return NULL; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (tmp->osmux.cid == cid && this->s_addr == from_addr->s_addr) |  | ||||||
| 			return tmp; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid); |  | ||||||
|  |  | ||||||
| 	return NULL; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void scheduled_tx_net_cb(struct msgb *msg, void *data) |  | ||||||
| { |  | ||||||
| 	struct mgcp_endpoint *endp = data; |  | ||||||
| 	struct sockaddr_in addr = { |  | ||||||
| 		.sin_addr = endp->net_end.addr, |  | ||||||
| 		.sin_port = endp->net_end.rtp_port, |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	endp->bts_end.octets += msg->len; |  | ||||||
| 	endp->bts_end.packets++; |  | ||||||
|  |  | ||||||
| 	mgcp_send(endp, MGCP_DEST_NET, 1, &addr, (char *)msg->data, msg->len); |  | ||||||
| 	msgb_free(msg); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void scheduled_tx_bts_cb(struct msgb *msg, void *data) |  | ||||||
| { |  | ||||||
| 	struct mgcp_endpoint *endp = data; |  | ||||||
| 	struct sockaddr_in addr = { |  | ||||||
| 		.sin_addr = endp->bts_end.addr, |  | ||||||
| 		.sin_port = endp->bts_end.rtp_port, |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	endp->net_end.octets += msg->len; |  | ||||||
| 	endp->net_end.packets++; |  | ||||||
|  |  | ||||||
| 	mgcp_send(endp, MGCP_DEST_BTS, 1, &addr, (char *)msg->data, msg->len); |  | ||||||
| 	msgb_free(msg); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr) |  | ||||||
| { |  | ||||||
| 	struct msgb *msg; |  | ||||||
| 	socklen_t slen = sizeof(*addr); |  | ||||||
| 	int ret; |  | ||||||
|  |  | ||||||
| 	msg = msgb_alloc(4096, "OSMUX"); |  | ||||||
| 	if (!msg) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot allocate message\n"); |  | ||||||
| 		return NULL; |  | ||||||
| 	} |  | ||||||
| 	ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0, |  | ||||||
| 			(struct sockaddr *)addr, &slen); |  | ||||||
| 	if (ret <= 0) { |  | ||||||
| 		msgb_free(msg); |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot receive message\n"); |  | ||||||
| 		return NULL; |  | ||||||
| 	} |  | ||||||
| 	msgb_put(msg, ret); |  | ||||||
|  |  | ||||||
| 	return msg; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #define osmux_chunk_length(msg, rem) (rem - msg->len); |  | ||||||
|  |  | ||||||
| int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what) |  | ||||||
| { |  | ||||||
| 	struct msgb *msg; |  | ||||||
| 	struct osmux_hdr *osmuxh; |  | ||||||
| 	struct llist_head list; |  | ||||||
| 	struct sockaddr_in addr; |  | ||||||
| 	struct mgcp_config *cfg = ofd->data; |  | ||||||
| 	uint32_t rem; |  | ||||||
|  |  | ||||||
| 	msg = osmux_recv(ofd, &addr); |  | ||||||
| 	if (!msg) |  | ||||||
| 		return -1; |  | ||||||
|  |  | ||||||
| 	/* not any further processing dummy messages */ |  | ||||||
| 	if (msg->data[0] == MGCP_DUMMY_LOAD) |  | ||||||
| 		goto out; |  | ||||||
|  |  | ||||||
| 	rem = msg->len; |  | ||||||
| 	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) { |  | ||||||
| 		struct mgcp_endpoint *endp; |  | ||||||
|  |  | ||||||
| 		/* Yes, we use MGCP_DEST_NET to locate the origin */ |  | ||||||
| 		endp = endpoint_lookup(cfg, osmuxh->circuit_id, |  | ||||||
| 				       &addr.sin_addr, MGCP_DEST_NET); |  | ||||||
| 		if (!endp) { |  | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 			     "Cannot find an endpoint for circuit_id=%d\n", |  | ||||||
| 			     osmuxh->circuit_id); |  | ||||||
| 			goto out; |  | ||||||
| 		} |  | ||||||
| 		endp->osmux.stats.octets += osmux_chunk_length(msg, rem); |  | ||||||
| 		endp->osmux.stats.chunks++; |  | ||||||
| 		rem = msg->len; |  | ||||||
|  |  | ||||||
| 		osmux_xfrm_output(osmuxh, &endp->osmux.out, &list); |  | ||||||
| 		osmux_tx_sched(&list, scheduled_tx_bts_cb, endp); |  | ||||||
| 	} |  | ||||||
| out: |  | ||||||
| 	msgb_free(msg); |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* This is called from the bsc-nat */ |  | ||||||
| static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr, |  | ||||||
| 			      struct msgb *msg) |  | ||||||
| { |  | ||||||
| 	struct mgcp_endpoint *endp; |  | ||||||
| 	uint8_t osmux_cid; |  | ||||||
|  |  | ||||||
| 	if (msg->len < 1 + sizeof(osmux_cid)) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 		     "Discarding truncated Osmux dummy load\n"); |  | ||||||
| 		goto out; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n", |  | ||||||
| 	     inet_ntoa(addr->sin_addr)); |  | ||||||
|  |  | ||||||
| 	if (!cfg->osmux) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 		     "bsc wants to use Osmux but bsc-nat did not request it\n"); |  | ||||||
| 		goto out; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* extract the osmux CID from the dummy message */ |  | ||||||
| 	memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid)); |  | ||||||
|  |  | ||||||
| 	endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS); |  | ||||||
| 	if (!endp) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 		     "Cannot find endpoint for Osmux CID %d\n", osmux_cid); |  | ||||||
| 		goto out; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (endp->osmux.state == OSMUX_STATE_ENABLED) |  | ||||||
| 		goto out; |  | ||||||
|  |  | ||||||
| 	if (osmux_enable_endpoint(endp, &addr->sin_addr, addr->sin_port) < 0 ) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 		     "Could not enable osmux in endpoint %d\n", |  | ||||||
| 		     ENDPOINT_NUMBER(endp)); |  | ||||||
| 		goto out; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	LOGP(DLMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n", |  | ||||||
| 	     ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr), |  | ||||||
| 	     ntohs(addr->sin_port)); |  | ||||||
| out: |  | ||||||
| 	msgb_free(msg); |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what) |  | ||||||
| { |  | ||||||
| 	struct msgb *msg; |  | ||||||
| 	struct osmux_hdr *osmuxh; |  | ||||||
| 	struct llist_head list; |  | ||||||
| 	struct sockaddr_in addr; |  | ||||||
| 	struct mgcp_config *cfg = ofd->data; |  | ||||||
| 	uint32_t rem; |  | ||||||
|  |  | ||||||
| 	msg = osmux_recv(ofd, &addr); |  | ||||||
| 	if (!msg) |  | ||||||
| 		return -1; |  | ||||||
|  |  | ||||||
| 	/* not any further processing dummy messages */ |  | ||||||
| 	if (msg->data[0] == MGCP_DUMMY_LOAD) |  | ||||||
| 		return osmux_handle_dummy(cfg, &addr, msg); |  | ||||||
|  |  | ||||||
| 	rem = msg->len; |  | ||||||
| 	while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) { |  | ||||||
| 		struct mgcp_endpoint *endp; |  | ||||||
|  |  | ||||||
| 		/* Yes, we use MGCP_DEST_BTS to locate the origin */ |  | ||||||
| 		endp = endpoint_lookup(cfg, osmuxh->circuit_id, |  | ||||||
| 				       &addr.sin_addr, MGCP_DEST_BTS); |  | ||||||
| 		if (!endp) { |  | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 			     "Cannot find an endpoint for circuit_id=%d\n", |  | ||||||
| 			     osmuxh->circuit_id); |  | ||||||
| 			goto out; |  | ||||||
| 		} |  | ||||||
| 		endp->osmux.stats.octets += osmux_chunk_length(msg, rem); |  | ||||||
| 		endp->osmux.stats.chunks++; |  | ||||||
| 		rem = msg->len; |  | ||||||
|  |  | ||||||
| 		osmux_xfrm_output(osmuxh, &endp->osmux.out, &list); |  | ||||||
| 		osmux_tx_sched(&list, scheduled_tx_net_cb, endp); |  | ||||||
| 	} |  | ||||||
| out: |  | ||||||
| 	msgb_free(msg); |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int osmux_init(int role, struct mgcp_config *cfg) |  | ||||||
| { |  | ||||||
| 	int ret; |  | ||||||
|  |  | ||||||
| 	switch(role) { |  | ||||||
| 	case OSMUX_ROLE_BSC: |  | ||||||
| 		osmux_fd.cb = osmux_read_from_bsc_nat_cb; |  | ||||||
| 		break; |  | ||||||
| 	case OSMUX_ROLE_BSC_NAT: |  | ||||||
| 		osmux_fd.cb = osmux_read_from_bsc_cb; |  | ||||||
| 		break; |  | ||||||
| 	default: |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "wrong role for OSMUX\n"); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
| 	osmux_fd.data = cfg; |  | ||||||
|  |  | ||||||
| 	ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port); |  | ||||||
| 	if (ret < 0) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n"); |  | ||||||
| 		return ret; |  | ||||||
| 	} |  | ||||||
| 	mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp); |  | ||||||
| 	osmux_fd.when |= BSC_FD_READ; |  | ||||||
|  |  | ||||||
| 	ret = osmo_fd_register(&osmux_fd); |  | ||||||
| 	if (ret < 0) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "cannot register OSMUX socket\n"); |  | ||||||
| 		return ret; |  | ||||||
| 	} |  | ||||||
| 	cfg->osmux_init = 1; |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int osmux_enable_endpoint(struct mgcp_endpoint *endp, struct in_addr *addr, uint16_t port) |  | ||||||
| { |  | ||||||
| 	/* If osmux is enabled, initialize the output handler. This handler is |  | ||||||
| 	 * used to reconstruct the RTP flow from osmux. The RTP SSRC is |  | ||||||
| 	 * allocated based on the circuit ID (endp->osmux.cid), which is unique |  | ||||||
| 	 * in the local scope to the BSC/BSC-NAT. We use it to divide the RTP |  | ||||||
| 	 * SSRC space (2^32) by the 256 possible circuit IDs, then randomly |  | ||||||
| 	 * select one value from that window. Thus, we have no chance to have |  | ||||||
| 	 * overlapping RTP SSRC traveling to the BTSes behind the BSC, |  | ||||||
| 	 * similarly, for flows traveling to the MSC. |  | ||||||
| 	 */ |  | ||||||
| 	static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256; |  | ||||||
|  |  | ||||||
| 	if (endp->osmux.state == OSMUX_STATE_DISABLED) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Endpoint %u didn't request Osmux\n", |  | ||||||
| 		     ENDPOINT_NUMBER(endp)); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	osmux_xfrm_output_init(&endp->osmux.out, |  | ||||||
| 			       (endp->osmux.cid * rtp_ssrc_winlen) + |  | ||||||
| 			       (random() % rtp_ssrc_winlen)); |  | ||||||
|  |  | ||||||
| 	endp->osmux.in = osmux_handle_lookup(endp->cfg, addr, port); |  | ||||||
| 	if (!endp->osmux.in) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot allocate input osmux handle\n"); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
| 	if (!osmux_xfrm_input_open_circuit(endp->osmux.in, endp->osmux.cid, |  | ||||||
| 					   endp->cfg->osmux_dummy)) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, "Cannot open osmux circuit %u\n", |  | ||||||
| 		     endp->osmux.cid); |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	switch (endp->cfg->role) { |  | ||||||
| 		case MGCP_BSC_NAT: |  | ||||||
| 			endp->type = MGCP_OSMUX_BSC_NAT; |  | ||||||
| 			break; |  | ||||||
| 		case MGCP_BSC: |  | ||||||
| 			endp->type = MGCP_OSMUX_BSC; |  | ||||||
| 			break; |  | ||||||
| 	} |  | ||||||
| 	endp->osmux.state = OSMUX_STATE_ENABLED; |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void osmux_disable_endpoint(struct mgcp_endpoint *endp) |  | ||||||
| { |  | ||||||
| 	LOGP(DLMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n", |  | ||||||
| 	     ENDPOINT_NUMBER(endp), endp->osmux.cid); |  | ||||||
| 	osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid); |  | ||||||
| 	endp->osmux.state = OSMUX_STATE_DISABLED; |  | ||||||
| 	endp->osmux.cid = -1; |  | ||||||
| 	osmux_handle_put(endp->osmux.in); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void osmux_release_cid(struct mgcp_endpoint *endp) |  | ||||||
| { |  | ||||||
| 	if (endp->osmux.allocated_cid >= 0) |  | ||||||
| 		osmux_put_cid(endp->osmux.allocated_cid); |  | ||||||
| 	endp->osmux.allocated_cid = -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void osmux_allocate_cid(struct mgcp_endpoint *endp) |  | ||||||
| { |  | ||||||
| 	osmux_release_cid(endp); |  | ||||||
| 	endp->osmux.allocated_cid = osmux_get_cid(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* We don't need to send the dummy load for osmux so often as another endpoint |  | ||||||
|  * may have already punched the hole in the firewall. This approach is simple |  | ||||||
|  * though. |  | ||||||
|  */ |  | ||||||
| int osmux_send_dummy(struct mgcp_endpoint *endp) |  | ||||||
| { |  | ||||||
| 	char buf[1 + sizeof(uint8_t)]; |  | ||||||
| 	struct in_addr addr_unset = {}; |  | ||||||
|  |  | ||||||
| 	buf[0] = MGCP_DUMMY_LOAD; |  | ||||||
| 	memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid)); |  | ||||||
|  |  | ||||||
| 	/* Wait until we have the connection information from MDCX */ |  | ||||||
| 	if (memcmp(&endp->net_end.addr, &addr_unset, sizeof(addr_unset)) == 0) |  | ||||||
| 		return 0; |  | ||||||
|  |  | ||||||
| 	if (endp->osmux.state == OSMUX_STATE_ACTIVATING) { |  | ||||||
| 		if (osmux_enable_endpoint(endp, &endp->net_end.addr, |  | ||||||
| 					  htons(endp->cfg->osmux_port)) < 0) { |  | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 			     "Could not activate osmux in endpoint %d\n", |  | ||||||
| 			     ENDPOINT_NUMBER(endp)); |  | ||||||
| 		} |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 		     "Osmux CID %u for %s:%u is now enabled\n", |  | ||||||
| 		     endp->osmux.cid, inet_ntoa(endp->net_end.addr), |  | ||||||
| 		     endp->cfg->osmux_port); |  | ||||||
| 	} |  | ||||||
| 	LOGP(DLMGCP, LOGL_DEBUG, |  | ||||||
| 	     "sending OSMUX dummy load to %s CID %u\n", |  | ||||||
| 	     inet_ntoa(endp->net_end.addr), endp->osmux.cid); |  | ||||||
|  |  | ||||||
| 	return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr, |  | ||||||
| 			     htons(endp->cfg->osmux_port), buf, sizeof(buf)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* bsc-nat allocates/releases the Osmux circuit ID */ |  | ||||||
| static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8]; |  | ||||||
|  |  | ||||||
| int osmux_used_cid(void) |  | ||||||
| { |  | ||||||
| 	int i, j, used = 0; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) { |  | ||||||
| 		for (j = 0; j < 8; j++) { |  | ||||||
| 			if (osmux_cid_bitmap[i] & (1 << j)) |  | ||||||
| 				used += 1; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return used; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int osmux_get_cid(void) |  | ||||||
| { |  | ||||||
| 	int i, j; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < sizeof(osmux_cid_bitmap); i++) { |  | ||||||
| 		for (j = 0; j < 8; j++) { |  | ||||||
| 			if (osmux_cid_bitmap[i] & (1 << j)) |  | ||||||
| 				continue; |  | ||||||
|  |  | ||||||
| 			osmux_cid_bitmap[i] |= (1 << j); |  | ||||||
| 			LOGP(DLMGCP, LOGL_DEBUG, |  | ||||||
| 			     "Allocating Osmux CID %u from pool\n", (i * 8) + j); |  | ||||||
| 			return (i * 8) + j; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	LOGP(DLMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n"); |  | ||||||
| 	return -1; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void osmux_put_cid(uint8_t osmux_cid) |  | ||||||
| { |  | ||||||
| 	LOGP(DLMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid); |  | ||||||
| 	osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8)); |  | ||||||
| } |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,305 +0,0 @@ | |||||||
| /* |  | ||||||
|  * Some SDP file parsing... |  | ||||||
|  * |  | ||||||
|  * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org> |  | ||||||
|  * (C) 2009-2014 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 Affero 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 Affero General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU Affero General Public License |  | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <osmocom/legacy_mgcp/mgcp.h> |  | ||||||
| #include <osmocom/legacy_mgcp/mgcp_internal.h> |  | ||||||
|  |  | ||||||
| #include <errno.h> |  | ||||||
|  |  | ||||||
| struct sdp_rtp_map { |  | ||||||
| 	/* the type */ |  | ||||||
| 	int payload_type; |  | ||||||
| 	/* null, static or later dynamic codec name */ |  | ||||||
| 	char *codec_name; |  | ||||||
| 	/* A pointer to the original line for later parsing */ |  | ||||||
| 	char *map_line; |  | ||||||
|  |  | ||||||
| 	int rate; |  | ||||||
| 	int channels; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec, |  | ||||||
| 			int payload_type, const char *audio_name) |  | ||||||
| { |  | ||||||
| 	int rate = codec->rate; |  | ||||||
| 	int channels = codec->channels; |  | ||||||
| 	char audio_codec[64]; |  | ||||||
|  |  | ||||||
| 	talloc_free(codec->subtype_name); |  | ||||||
| 	codec->subtype_name = NULL; |  | ||||||
| 	talloc_free(codec->audio_name); |  | ||||||
| 	codec->audio_name = NULL; |  | ||||||
|  |  | ||||||
| 	if (payload_type != PTYPE_UNDEFINED) |  | ||||||
| 		codec->payload_type = payload_type; |  | ||||||
|  |  | ||||||
| 	if (!audio_name) { |  | ||||||
| 		switch (payload_type) { |  | ||||||
| 		case 0: audio_name = "PCMU/8000/1"; break; |  | ||||||
| 		case 3: audio_name = "GSM/8000/1"; break; |  | ||||||
| 		case 8: audio_name = "PCMA/8000/1"; break; |  | ||||||
| 		case 18: audio_name = "G729/8000/1"; break; |  | ||||||
| 		default: |  | ||||||
| 			 /* Payload type is unknown, don't change rate and |  | ||||||
| 			  * channels. */ |  | ||||||
| 			 /* TODO: return value? */ |  | ||||||
| 			 return 0; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (sscanf(audio_name, "%63[^/]/%d/%d", |  | ||||||
| 		   audio_codec, &rate, &channels) < 1) |  | ||||||
| 		return -EINVAL; |  | ||||||
|  |  | ||||||
| 	codec->rate = rate; |  | ||||||
| 	codec->channels = channels; |  | ||||||
| 	codec->subtype_name = talloc_strdup(ctx, audio_codec); |  | ||||||
| 	codec->audio_name = talloc_strdup(ctx, audio_name); |  | ||||||
|  |  | ||||||
| 	if (!strcmp(audio_codec, "G729")) { |  | ||||||
| 		codec->frame_duration_num = 10; |  | ||||||
| 		codec->frame_duration_den = 1000; |  | ||||||
| 	} else { |  | ||||||
| 		codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; |  | ||||||
| 		codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (payload_type < 0) { |  | ||||||
| 		payload_type = 96; |  | ||||||
| 		if (rate == 8000 && channels == 1) { |  | ||||||
| 			if (!strcmp(audio_codec, "GSM")) |  | ||||||
| 				payload_type = 3; |  | ||||||
| 			else if (!strcmp(audio_codec, "PCMA")) |  | ||||||
| 				payload_type = 8; |  | ||||||
| 			else if (!strcmp(audio_codec, "PCMU")) |  | ||||||
| 				payload_type = 0; |  | ||||||
| 			else if (!strcmp(audio_codec, "G729")) |  | ||||||
| 				payload_type = 18; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		codec->payload_type = payload_type; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (channels != 1) |  | ||||||
| 		LOGP(DLMGCP, LOGL_NOTICE, |  | ||||||
| 		     "Channels != 1 in SDP: '%s'\n", audio_name); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < used; ++i) { |  | ||||||
| 		switch (codecs[i].payload_type) { |  | ||||||
| 		case 0: |  | ||||||
| 			codecs[i].codec_name = "PCMU"; |  | ||||||
| 			codecs[i].rate = 8000; |  | ||||||
| 			codecs[i].channels = 1; |  | ||||||
| 			break; |  | ||||||
| 		case 3: |  | ||||||
| 			codecs[i].codec_name = "GSM"; |  | ||||||
| 			codecs[i].rate = 8000; |  | ||||||
| 			codecs[i].channels = 1; |  | ||||||
| 			break; |  | ||||||
| 		case 8: |  | ||||||
| 			codecs[i].codec_name = "PCMA"; |  | ||||||
| 			codecs[i].rate = 8000; |  | ||||||
| 			codecs[i].channels = 1; |  | ||||||
| 			break; |  | ||||||
| 		case 18: |  | ||||||
| 			codecs[i].codec_name = "G729"; |  | ||||||
| 			codecs[i].rate = 8000; |  | ||||||
| 			codecs[i].channels = 1; |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, char *audio_name) |  | ||||||
| { |  | ||||||
| 	int i; |  | ||||||
|  |  | ||||||
| 	for (i = 0; i < used; ++i) { |  | ||||||
| 		char audio_codec[64]; |  | ||||||
| 		int rate = -1; |  | ||||||
| 		int channels = -1; |  | ||||||
| 		if (codecs[i].payload_type != payload) |  | ||||||
| 			continue; |  | ||||||
| 		if (sscanf(audio_name, "%63[^/]/%d/%d", |  | ||||||
| 				audio_codec, &rate, &channels) < 1) { |  | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, "Failed to parse '%s'\n", audio_name); |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		codecs[i].map_line = talloc_strdup(ctx, audio_name); |  | ||||||
| 		codecs[i].codec_name = talloc_strdup(ctx, audio_codec); |  | ||||||
| 		codecs[i].rate = rate; |  | ||||||
| 		codecs[i].channels = channels; |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	LOGP(DLMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int is_codec_compatible(struct mgcp_endpoint *endp, struct sdp_rtp_map *codec) |  | ||||||
| { |  | ||||||
| 	char *bts_codec; |  | ||||||
| 	char audio_codec[64]; |  | ||||||
|  |  | ||||||
| 	if (!codec->codec_name) |  | ||||||
| 		return 0; |  | ||||||
|  |  | ||||||
| 	/* |  | ||||||
| 	 * GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go |  | ||||||
| 	 * by name first. |  | ||||||
| 	 */ |  | ||||||
| 	bts_codec = endp->tcfg->audio_name; |  | ||||||
| 	if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1) |  | ||||||
| 		return 0; |  | ||||||
|  |  | ||||||
| 	return strcasecmp(audio_codec, codec->codec_name) == 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) |  | ||||||
| { |  | ||||||
| 	struct sdp_rtp_map codecs[10]; |  | ||||||
| 	int codecs_used = 0; |  | ||||||
| 	char *line; |  | ||||||
| 	int maxptime = -1; |  | ||||||
| 	int i; |  | ||||||
| 	int codecs_assigned = 0; |  | ||||||
| 	void *tmp_ctx = talloc_new(NULL); |  | ||||||
|  |  | ||||||
| 	memset(&codecs, 0, sizeof(codecs)); |  | ||||||
|  |  | ||||||
| 	for_each_line(line, p->save) { |  | ||||||
| 		switch (line[0]) { |  | ||||||
| 		case 'o': |  | ||||||
| 		case 's': |  | ||||||
| 		case 't': |  | ||||||
| 		case 'v': |  | ||||||
| 			/* skip these SDP attributes */ |  | ||||||
| 			break; |  | ||||||
| 		case 'a': { |  | ||||||
| 			int payload; |  | ||||||
| 			int ptime, ptime2 = 0; |  | ||||||
| 			char audio_name[64]; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 			if (sscanf(line, "a=rtpmap:%d %63s", |  | ||||||
| 				   &payload, audio_name) == 2) { |  | ||||||
| 				codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name); |  | ||||||
| 			} else if (sscanf(line, "a=ptime:%d-%d", |  | ||||||
| 					  &ptime, &ptime2) >= 1) { |  | ||||||
| 				if (ptime2 > 0 && ptime2 != ptime) |  | ||||||
| 					rtp->packet_duration_ms = 0; |  | ||||||
| 				else |  | ||||||
| 					rtp->packet_duration_ms = ptime; |  | ||||||
| 			} else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) { |  | ||||||
| 				maxptime = ptime2; |  | ||||||
| 			} |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 		case 'm': { |  | ||||||
| 			int port, rc; |  | ||||||
|  |  | ||||||
| 			rc = sscanf(line, "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d", |  | ||||||
| 					&port, |  | ||||||
| 					&codecs[0].payload_type, |  | ||||||
| 					&codecs[1].payload_type, |  | ||||||
| 					&codecs[2].payload_type, |  | ||||||
| 					&codecs[3].payload_type, |  | ||||||
| 					&codecs[4].payload_type, |  | ||||||
| 					&codecs[5].payload_type, |  | ||||||
| 					&codecs[6].payload_type, |  | ||||||
| 					&codecs[7].payload_type, |  | ||||||
| 					&codecs[8].payload_type, |  | ||||||
| 					&codecs[9].payload_type); |  | ||||||
| 			if (rc >= 2) { |  | ||||||
| 				rtp->rtp_port = htons(port); |  | ||||||
| 				rtp->rtcp_port = htons(port + 1); |  | ||||||
| 				codecs_used = rc - 1; |  | ||||||
| 				codecs_initialize(tmp_ctx, codecs, codecs_used); |  | ||||||
| 			} |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 		case 'c': { |  | ||||||
| 			char ipv4[16]; |  | ||||||
|  |  | ||||||
| 			if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) { |  | ||||||
| 				inet_aton(ipv4, &rtp->addr); |  | ||||||
| 			} |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 		default: |  | ||||||
| 			if (p->endp) |  | ||||||
| 				LOGP(DLMGCP, LOGL_NOTICE, |  | ||||||
| 				     "Unhandled SDP option: '%c'/%d on 0x%x\n", |  | ||||||
| 				     line[0], line[0], ENDPOINT_NUMBER(p->endp)); |  | ||||||
| 			else |  | ||||||
| 				LOGP(DLMGCP, LOGL_NOTICE, |  | ||||||
| 				     "Unhandled SDP option: '%c'/%d\n", |  | ||||||
| 				     line[0], line[0]); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* Now select the primary and alt_codec */ |  | ||||||
| 	for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) { |  | ||||||
| 		struct mgcp_rtp_codec *codec = codecs_assigned == 0 ? |  | ||||||
| 					&rtp->codec : &rtp->alt_codec; |  | ||||||
|  |  | ||||||
| 		if (endp->tcfg->no_audio_transcoding && |  | ||||||
| 			!is_codec_compatible(endp, &codecs[i])) { |  | ||||||
| 			LOGP(DLMGCP, LOGL_NOTICE, "Skipping codec %s\n", |  | ||||||
| 				codecs[i].codec_name); |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		mgcp_set_audio_info(p->cfg, codec, |  | ||||||
| 					codecs[i].payload_type, |  | ||||||
| 					codecs[i].map_line); |  | ||||||
| 		codecs_assigned += 1; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (codecs_assigned > 0) { |  | ||||||
| 		/* TODO/XXX: Store this per codec and derive it on use */ |  | ||||||
| 		if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den > |  | ||||||
| 				rtp->codec.frame_duration_num * 1500) { |  | ||||||
| 			/* more than 1 frame */ |  | ||||||
| 			rtp->packet_duration_ms = 0; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		LOGP(DLMGCP, LOGL_NOTICE, |  | ||||||
| 		     "Got media info via SDP: port %d, payload %d (%s), " |  | ||||||
| 		     "duration %d, addr %s\n", |  | ||||||
| 		     ntohs(rtp->rtp_port), rtp->codec.payload_type, |  | ||||||
| 		     rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown", |  | ||||||
| 		     rtp->packet_duration_ms, inet_ntoa(rtp->addr)); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	talloc_free(tmp_ctx); |  | ||||||
| 	return codecs_assigned > 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @@ -1,611 +0,0 @@ | |||||||
| /* |  | ||||||
|  * (C) 2014 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 Affero 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 Affero General Public License for more details. |  | ||||||
|  * |  | ||||||
|  * You should have received a copy of the GNU Affero General Public License |  | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  | ||||||
|  * |  | ||||||
|  */ |  | ||||||
|  |  | ||||||
| #include <stdlib.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <errno.h> |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #include "g711common.h" |  | ||||||
|  |  | ||||||
| #include <osmocom/legacy_mgcp/mgcp.h> |  | ||||||
| #include <osmocom/legacy_mgcp/mgcp_internal.h> |  | ||||||
| #include <osmocom/legacy_mgcp/mgcp_transcode.h> |  | ||||||
|  |  | ||||||
| #include <osmocom/core/talloc.h> |  | ||||||
| #include <osmocom/netif/rtp.h> |  | ||||||
|  |  | ||||||
| int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst) |  | ||||||
| { |  | ||||||
| 	struct mgcp_process_rtp_state *state = state_; |  | ||||||
| 	if (dst) |  | ||||||
| 		return (nsamples >= 0 ? |  | ||||||
| 			nsamples / state->dst_samples_per_frame : |  | ||||||
| 			1) * state->dst_frame_size; |  | ||||||
| 	else |  | ||||||
| 		return (nsamples >= 0 ? |  | ||||||
| 			nsamples / state->src_samples_per_frame : |  | ||||||
| 			1) * state->src_frame_size; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static enum audio_format get_audio_format(const struct mgcp_rtp_codec *codec) |  | ||||||
| { |  | ||||||
| 	if (codec->subtype_name) { |  | ||||||
| 		if (!strcasecmp("GSM", codec->subtype_name)) |  | ||||||
| 			return AF_GSM; |  | ||||||
| 		if (!strcasecmp("PCMA", codec->subtype_name)) |  | ||||||
| 			return AF_PCMA; |  | ||||||
| 		if (!strcasecmp("PCMU", codec->subtype_name)) |  | ||||||
| 			return AF_PCMU; |  | ||||||
| #ifdef HAVE_BCG729 |  | ||||||
| 		if (!strcasecmp("G729", codec->subtype_name)) |  | ||||||
| 			return AF_G729; |  | ||||||
| #endif |  | ||||||
| 		if (!strcasecmp("L16", codec->subtype_name)) |  | ||||||
| 			return AF_L16; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	switch (codec->payload_type) { |  | ||||||
| 	case 0 /* PCMU */: |  | ||||||
| 		return AF_PCMU; |  | ||||||
| 	case 3 /* GSM */: |  | ||||||
| 		return AF_GSM; |  | ||||||
| 	case 8 /* PCMA */: |  | ||||||
| 		return AF_PCMA; |  | ||||||
| #ifdef HAVE_BCG729 |  | ||||||
| 	case 18 /* G.729 */: |  | ||||||
| 		return AF_G729; |  | ||||||
| #endif |  | ||||||
| 	case 11 /* L16 */: |  | ||||||
| 		return AF_L16; |  | ||||||
| 	default: |  | ||||||
| 		return AF_INVALID; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void l16_encode(short *sample, unsigned char *buf, size_t n) |  | ||||||
| { |  | ||||||
| 	for (; n > 0; --n, ++sample, buf += 2) { |  | ||||||
| 		buf[0] = sample[0] >> 8; |  | ||||||
| 		buf[1] = sample[0] & 0xff; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void l16_decode(unsigned char *buf, short *sample, size_t n) |  | ||||||
| { |  | ||||||
| 	for (; n > 0; --n, ++sample, buf += 2) |  | ||||||
| 		sample[0] = ((short)buf[0] << 8) | buf[1]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void alaw_encode(short *sample, unsigned char *buf, size_t n) |  | ||||||
| { |  | ||||||
| 	for (; n > 0; --n) |  | ||||||
| 		*(buf++) = s16_to_alaw(*(sample++)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void alaw_decode(unsigned char *buf, short *sample, size_t n) |  | ||||||
| { |  | ||||||
| 	for (; n > 0; --n) |  | ||||||
| 		*(sample++) = alaw_to_s16(*(buf++)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void ulaw_encode(short *sample, unsigned char *buf, size_t n) |  | ||||||
| { |  | ||||||
| 	for (; n > 0; --n) |  | ||||||
| 		*(buf++) = s16_to_ulaw(*(sample++)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void ulaw_decode(unsigned char *buf, short *sample, size_t n) |  | ||||||
| { |  | ||||||
| 	for (; n > 0; --n) |  | ||||||
| 		*(sample++) = ulaw_to_s16(*(buf++)); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int processing_state_destructor(struct mgcp_process_rtp_state *state) |  | ||||||
| { |  | ||||||
| 	switch (state->src_fmt) { |  | ||||||
| 	case AF_GSM: |  | ||||||
| 		if (state->src.gsm_handle) |  | ||||||
| 			gsm_destroy(state->src.gsm_handle); |  | ||||||
| 		break; |  | ||||||
| #ifdef HAVE_BCG729 |  | ||||||
| 	case AF_G729: |  | ||||||
| 		if (state->src.g729_dec) |  | ||||||
| 			closeBcg729DecoderChannel(state->src.g729_dec); |  | ||||||
| 		break; |  | ||||||
| #endif |  | ||||||
| 	default: |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
| 	switch (state->dst_fmt) { |  | ||||||
| 	case AF_GSM: |  | ||||||
| 		if (state->dst.gsm_handle) |  | ||||||
| 			gsm_destroy(state->dst.gsm_handle); |  | ||||||
| 		break; |  | ||||||
| #ifdef HAVE_BCG729 |  | ||||||
| 	case AF_G729: |  | ||||||
| 		if (state->dst.g729_enc) |  | ||||||
| 			closeBcg729EncoderChannel(state->dst.g729_enc); |  | ||||||
| 		break; |  | ||||||
| #endif |  | ||||||
| 	default: |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int mgcp_transcoding_setup(struct mgcp_endpoint *endp, |  | ||||||
| 			   struct mgcp_rtp_end *dst_end, |  | ||||||
| 			   struct mgcp_rtp_end *src_end) |  | ||||||
| { |  | ||||||
| 	struct mgcp_process_rtp_state *state; |  | ||||||
| 	enum audio_format src_fmt, dst_fmt; |  | ||||||
| 	const struct mgcp_rtp_codec *dst_codec = &dst_end->codec; |  | ||||||
|  |  | ||||||
| 	/* cleanup first */ |  | ||||||
| 	if (dst_end->rtp_process_data) { |  | ||||||
| 		talloc_free(dst_end->rtp_process_data); |  | ||||||
| 		dst_end->rtp_process_data = NULL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (!src_end) |  | ||||||
| 		return 0; |  | ||||||
|  |  | ||||||
| 	const struct mgcp_rtp_codec *src_codec = &src_end->codec; |  | ||||||
|  |  | ||||||
| 	if (endp->tcfg->no_audio_transcoding) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_NOTICE, |  | ||||||
| 			"Transcoding disabled on endpoint 0x%x\n", |  | ||||||
| 			ENDPOINT_NUMBER(endp)); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	src_fmt = get_audio_format(src_codec); |  | ||||||
| 	dst_fmt = get_audio_format(dst_codec); |  | ||||||
|  |  | ||||||
| 	LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 	     "Checking transcoding: %s (%d) -> %s (%d)\n", |  | ||||||
| 	     src_codec->subtype_name, src_codec->payload_type, |  | ||||||
| 	     dst_codec->subtype_name, dst_codec->payload_type); |  | ||||||
|  |  | ||||||
| 	if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) { |  | ||||||
| 		if (!src_codec->subtype_name || !dst_codec->subtype_name) |  | ||||||
| 			/* Not enough info, do nothing */ |  | ||||||
| 			return 0; |  | ||||||
|  |  | ||||||
| 		if (strcasecmp(src_codec->subtype_name, dst_codec->subtype_name) == 0) |  | ||||||
| 			/* Nothing to do */ |  | ||||||
| 			return 0; |  | ||||||
|  |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 		     "Cannot transcode: %s codec not supported (%s -> %s).\n", |  | ||||||
| 		     src_fmt != AF_INVALID ? "destination" : "source", |  | ||||||
| 		     src_codec->audio_name, dst_codec->audio_name); |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (src_codec->rate && dst_codec->rate && src_codec->rate != dst_codec->rate) { |  | ||||||
| 		LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 		     "Cannot transcode: rate conversion (%d -> %d) not supported.\n", |  | ||||||
| 		     src_codec->rate, dst_codec->rate); |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state); |  | ||||||
| 	talloc_set_destructor(state, processing_state_destructor); |  | ||||||
| 	dst_end->rtp_process_data = state; |  | ||||||
|  |  | ||||||
| 	state->src_fmt = src_fmt; |  | ||||||
|  |  | ||||||
| 	switch (state->src_fmt) { |  | ||||||
| 	case AF_L16: |  | ||||||
| 	case AF_S16: |  | ||||||
| 		state->src_frame_size = 80 * sizeof(short); |  | ||||||
| 		state->src_samples_per_frame = 80; |  | ||||||
| 		break; |  | ||||||
| 	case AF_GSM: |  | ||||||
| 		state->src_frame_size = sizeof(gsm_frame); |  | ||||||
| 		state->src_samples_per_frame = 160; |  | ||||||
| 		state->src.gsm_handle = gsm_create(); |  | ||||||
| 		if (!state->src.gsm_handle) { |  | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 			     "Failed to initialize GSM decoder.\n"); |  | ||||||
| 			return -EINVAL; |  | ||||||
| 		} |  | ||||||
| 		break; |  | ||||||
| #ifdef HAVE_BCG729 |  | ||||||
| 	case AF_G729: |  | ||||||
| 		state->src_frame_size = 10; |  | ||||||
| 		state->src_samples_per_frame = 80; |  | ||||||
| 		state->src.g729_dec = initBcg729DecoderChannel(); |  | ||||||
| 		if (!state->src.g729_dec) { |  | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 			     "Failed to initialize G.729 decoder.\n"); |  | ||||||
| 			return -EINVAL; |  | ||||||
| 		} |  | ||||||
| 		break; |  | ||||||
| #endif |  | ||||||
| 	case AF_PCMU: |  | ||||||
| 	case AF_PCMA: |  | ||||||
| 		state->src_frame_size = 80; |  | ||||||
| 		state->src_samples_per_frame = 80; |  | ||||||
| 		break; |  | ||||||
| 	default: |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	state->dst_fmt = dst_fmt; |  | ||||||
|  |  | ||||||
| 	switch (state->dst_fmt) { |  | ||||||
| 	case AF_L16: |  | ||||||
| 	case AF_S16: |  | ||||||
| 		state->dst_frame_size = 80*sizeof(short); |  | ||||||
| 		state->dst_samples_per_frame = 80; |  | ||||||
| 		break; |  | ||||||
| 	case AF_GSM: |  | ||||||
| 		state->dst_frame_size = sizeof(gsm_frame); |  | ||||||
| 		state->dst_samples_per_frame = 160; |  | ||||||
| 		state->dst.gsm_handle = gsm_create(); |  | ||||||
| 		if (!state->dst.gsm_handle) { |  | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 			     "Failed to initialize GSM encoder.\n"); |  | ||||||
| 			return -EINVAL; |  | ||||||
| 		} |  | ||||||
| 		break; |  | ||||||
| #ifdef HAVE_BCG729 |  | ||||||
| 	case AF_G729: |  | ||||||
| 		state->dst_frame_size = 10; |  | ||||||
| 		state->dst_samples_per_frame = 80; |  | ||||||
| 		state->dst.g729_enc = initBcg729EncoderChannel(); |  | ||||||
| 		if (!state->dst.g729_enc) { |  | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 			     "Failed to initialize G.729 decoder.\n"); |  | ||||||
| 			return -EINVAL; |  | ||||||
| 		} |  | ||||||
| 		break; |  | ||||||
| #endif |  | ||||||
| 	case AF_PCMU: |  | ||||||
| 	case AF_PCMA: |  | ||||||
| 		state->dst_frame_size = 80; |  | ||||||
| 		state->dst_samples_per_frame = 80; |  | ||||||
| 		break; |  | ||||||
| 	default: |  | ||||||
| 		break; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (dst_end->force_output_ptime) |  | ||||||
| 		state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end); |  | ||||||
|  |  | ||||||
| 	LOGP(DLMGCP, LOGL_INFO, |  | ||||||
| 	     "Initialized RTP processing on: 0x%x " |  | ||||||
| 	     "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n", |  | ||||||
| 	     ENDPOINT_NUMBER(endp), |  | ||||||
| 	     src_fmt, src_codec->payload_type, src_codec->rate, src_end->fmtp_extra, |  | ||||||
| 	     dst_fmt, dst_codec->payload_type, dst_codec->rate, dst_end->fmtp_extra); |  | ||||||
|  |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, |  | ||||||
| 					  int *payload_type, |  | ||||||
| 					  const char**audio_name, |  | ||||||
| 					  const char**fmtp_extra) |  | ||||||
| { |  | ||||||
| 	struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data; |  | ||||||
| 	struct mgcp_rtp_codec *net_codec = &endp->net_end.codec; |  | ||||||
| 	struct mgcp_rtp_codec *bts_codec = &endp->bts_end.codec; |  | ||||||
|  |  | ||||||
| 	if (!state || net_codec->payload_type < 0) { |  | ||||||
| 		*payload_type = bts_codec->payload_type; |  | ||||||
| 		*audio_name = bts_codec->audio_name; |  | ||||||
| 		*fmtp_extra = endp->bts_end.fmtp_extra; |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	*payload_type = net_codec->payload_type; |  | ||||||
| 	*audio_name = net_codec->audio_name; |  | ||||||
| 	*fmtp_extra = endp->net_end.fmtp_extra; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int decode_audio(struct mgcp_process_rtp_state *state, |  | ||||||
| 			uint8_t **src, size_t *nbytes) |  | ||||||
| { |  | ||||||
| 	while (*nbytes >= state->src_frame_size) { |  | ||||||
| 		if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) { |  | ||||||
| 			LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 			     "Sample buffer too small: %zu > %zu.\n", |  | ||||||
| 			     state->sample_cnt + state->src_samples_per_frame, |  | ||||||
| 			     ARRAY_SIZE(state->samples)); |  | ||||||
| 			return -ENOSPC; |  | ||||||
| 		} |  | ||||||
| 		switch (state->src_fmt) { |  | ||||||
| 		case AF_GSM: |  | ||||||
| 			if (gsm_decode(state->src.gsm_handle, |  | ||||||
| 				       (gsm_byte *)*src, state->samples + state->sample_cnt) < 0) { |  | ||||||
| 				LOGP(DLMGCP, LOGL_ERROR, |  | ||||||
| 				     "Failed to decode GSM.\n"); |  | ||||||
| 				return -EINVAL; |  | ||||||
| 			} |  | ||||||
| 			break; |  | ||||||
| #ifdef HAVE_BCG729 |  | ||||||
| 		case AF_G729: |  | ||||||
| 			bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt); |  | ||||||
| 			break; |  | ||||||
| #endif |  | ||||||
| 		case AF_PCMU: |  | ||||||
| 			ulaw_decode(*src, state->samples + state->sample_cnt, |  | ||||||
| 				    state->src_samples_per_frame); |  | ||||||
| 			break; |  | ||||||
| 		case AF_PCMA: |  | ||||||
| 			alaw_decode(*src, state->samples + state->sample_cnt, |  | ||||||
| 				    state->src_samples_per_frame); |  | ||||||
| 			break; |  | ||||||
| 		case AF_S16: |  | ||||||
| 			memmove(state->samples + state->sample_cnt, *src, |  | ||||||
| 				state->src_frame_size); |  | ||||||
| 			break; |  | ||||||
| 		case AF_L16: |  | ||||||
| 			l16_decode(*src, state->samples + state->sample_cnt, |  | ||||||
| 				   state->src_samples_per_frame); |  | ||||||
| 			break; |  | ||||||
| 		default: |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 		*src        += state->src_frame_size; |  | ||||||
| 		*nbytes     -= state->src_frame_size; |  | ||||||
| 		state->sample_cnt += state->src_samples_per_frame; |  | ||||||
| 	} |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static int encode_audio(struct mgcp_process_rtp_state *state, |  | ||||||
| 			uint8_t *dst, size_t buf_size, size_t max_samples) |  | ||||||
| { |  | ||||||
| 	int nbytes = 0; |  | ||||||
| 	size_t nsamples = 0; |  | ||||||
| 	/* Encode samples into dst */ |  | ||||||
| 	while (nsamples + state->dst_samples_per_frame <= max_samples) { |  | ||||||
| 		if (nbytes + state->dst_frame_size > buf_size) { |  | ||||||
| 			if (nbytes > 0) |  | ||||||
| 				break; |  | ||||||
|  |  | ||||||
| 			/* Not even one frame fits into the buffer */ |  | ||||||
| 			LOGP(DLMGCP, LOGL_INFO, |  | ||||||
| 			     "Encoding (RTP) buffer too small: %zu > %zu.\n", |  | ||||||
| 			     nbytes + state->dst_frame_size, buf_size); |  | ||||||
| 			return -ENOSPC; |  | ||||||
| 		} |  | ||||||
| 		switch (state->dst_fmt) { |  | ||||||
| 		case AF_GSM: |  | ||||||
| 			gsm_encode(state->dst.gsm_handle, |  | ||||||
| 				   state->samples + state->sample_offs, dst); |  | ||||||
| 			break; |  | ||||||
| #ifdef HAVE_BCG729 |  | ||||||
| 		case AF_G729: |  | ||||||
| 			bcg729Encoder(state->dst.g729_enc, |  | ||||||
| 				      state->samples + state->sample_offs, dst); |  | ||||||
| 			break; |  | ||||||
| #endif |  | ||||||
| 		case AF_PCMU: |  | ||||||
| 			ulaw_encode(state->samples + state->sample_offs, dst, |  | ||||||
| 				    state->src_samples_per_frame); |  | ||||||
| 			break; |  | ||||||
| 		case AF_PCMA: |  | ||||||
| 			alaw_encode(state->samples + state->sample_offs, dst, |  | ||||||
| 				    state->src_samples_per_frame); |  | ||||||
| 			break; |  | ||||||
| 		case AF_S16: |  | ||||||
| 			memmove(dst, state->samples + state->sample_offs, |  | ||||||
| 				state->dst_frame_size); |  | ||||||
| 			break; |  | ||||||
| 		case AF_L16: |  | ||||||
| 			l16_encode(state->samples + state->sample_offs, dst, |  | ||||||
| 				   state->src_samples_per_frame); |  | ||||||
| 			break; |  | ||||||
| 		default: |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
| 		dst        += state->dst_frame_size; |  | ||||||
| 		nbytes     += state->dst_frame_size; |  | ||||||
| 		state->sample_offs += state->dst_samples_per_frame; |  | ||||||
| 		nsamples   += state->dst_samples_per_frame; |  | ||||||
| 	} |  | ||||||
| 	state->sample_cnt -= nsamples; |  | ||||||
| 	return nbytes; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static struct mgcp_rtp_end *source_for_dest(struct mgcp_endpoint *endp, |  | ||||||
| 					struct mgcp_rtp_end *dst_end) |  | ||||||
| { |  | ||||||
| 	if (&endp->bts_end == dst_end) |  | ||||||
| 		return &endp->net_end; |  | ||||||
| 	else if (&endp->net_end == dst_end) |  | ||||||
| 		return &endp->bts_end; |  | ||||||
| 	OSMO_ASSERT(0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* |  | ||||||
|  * With some modems we get offered multiple codecs |  | ||||||
|  * and we have selected one of them. It might not |  | ||||||
|  * be the right one and we need to detect this with |  | ||||||
|  * the first audio packets. One difficulty is that |  | ||||||
|  * we patch the rtp payload type in place, so we |  | ||||||
|  * need to discuss this. |  | ||||||
|  */ |  | ||||||
| struct mgcp_process_rtp_state *check_transcode_state( |  | ||||||
| 				struct mgcp_endpoint *endp, |  | ||||||
| 				struct mgcp_rtp_end *dst_end, |  | ||||||
| 				struct rtp_hdr *rtp_hdr) |  | ||||||
| { |  | ||||||
| 	struct mgcp_rtp_end *src_end; |  | ||||||
|  |  | ||||||
| 	/* Only deal with messages from net to bts */ |  | ||||||
| 	if (&endp->bts_end != dst_end) |  | ||||||
| 		goto done; |  | ||||||
|  |  | ||||||
| 	src_end = source_for_dest(endp, dst_end); |  | ||||||
|  |  | ||||||
| 	/* Already patched */ |  | ||||||
| 	if (rtp_hdr->payload_type == dst_end->codec.payload_type) |  | ||||||
| 		goto done; |  | ||||||
| 	/* The payload we expect */ |  | ||||||
| 	if (rtp_hdr->payload_type == src_end->codec.payload_type) |  | ||||||
| 		goto done; |  | ||||||
| 	/* The matching alternate payload type? Then switch */ |  | ||||||
| 	if (rtp_hdr->payload_type == src_end->alt_codec.payload_type) { |  | ||||||
| 		struct mgcp_config *cfg = endp->cfg; |  | ||||||
| 		struct mgcp_rtp_codec tmp_codec = src_end->alt_codec; |  | ||||||
| 		src_end->alt_codec = src_end->codec; |  | ||||||
| 		src_end->codec = tmp_codec; |  | ||||||
| 		cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end); |  | ||||||
| 		cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| done: |  | ||||||
| 	return dst_end->rtp_process_data; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, |  | ||||||
| 				struct mgcp_rtp_end *dst_end, |  | ||||||
| 			     char *data, int *len, int buf_size) |  | ||||||
| { |  | ||||||
| 	struct mgcp_process_rtp_state *state; |  | ||||||
| 	const size_t rtp_hdr_size = sizeof(struct rtp_hdr); |  | ||||||
| 	struct rtp_hdr *rtp_hdr = (struct rtp_hdr *) data; |  | ||||||
| 	char *payload_data = (char *) &rtp_hdr->data[0]; |  | ||||||
| 	int payload_len = *len - rtp_hdr_size; |  | ||||||
| 	uint8_t *src = (uint8_t *)payload_data; |  | ||||||
| 	uint8_t *dst = (uint8_t *)payload_data; |  | ||||||
| 	size_t nbytes = payload_len; |  | ||||||
| 	size_t nsamples; |  | ||||||
| 	size_t max_samples; |  | ||||||
| 	uint32_t ts_no; |  | ||||||
| 	int rc; |  | ||||||
|  |  | ||||||
| 	state = check_transcode_state(endp, dst_end, rtp_hdr); |  | ||||||
| 	if (!state) |  | ||||||
| 		return 0; |  | ||||||
|  |  | ||||||
| 	if (state->src_fmt == state->dst_fmt) { |  | ||||||
| 		if (!state->dst_packet_duration) |  | ||||||
| 			return 0; |  | ||||||
|  |  | ||||||
| 		/* TODO: repackage without transcoding */ |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* If the remaining samples do not fit into a fixed ptime, |  | ||||||
| 	 * a) discard them, if the next packet is much later |  | ||||||
| 	 * b) add silence and * send it, if the current packet is not |  | ||||||
| 	 *    yet too late |  | ||||||
| 	 * c) append the sample data, if the timestamp matches exactly |  | ||||||
| 	 */ |  | ||||||
|  |  | ||||||
| 	/* TODO: check payload type (-> G.711 comfort noise) */ |  | ||||||
|  |  | ||||||
| 	if (payload_len > 0) { |  | ||||||
| 		ts_no = ntohl(rtp_hdr->timestamp); |  | ||||||
| 		if (!state->is_running) { |  | ||||||
| 			state->next_seq = ntohs(rtp_hdr->sequence); |  | ||||||
| 			state->next_time = ts_no; |  | ||||||
| 			state->is_running = 1; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 		if (state->sample_cnt > 0) { |  | ||||||
| 			int32_t delta = ts_no - state->next_time; |  | ||||||
| 			/* TODO: check sequence? reordering? packet loss? */ |  | ||||||
|  |  | ||||||
| 			if (delta > state->sample_cnt) { |  | ||||||
| 				/* There is a time gap between the last packet |  | ||||||
| 				 * and the current one. Just discard the |  | ||||||
| 				 * partial data that is left in the buffer. |  | ||||||
| 				 * TODO: This can be improved by adding silence |  | ||||||
| 				 * instead if the delta is small enough. |  | ||||||
| 				 */ |  | ||||||
| 				LOGP(DLMGCP, LOGL_NOTICE, |  | ||||||
| 					"0x%x dropping sample buffer due delta=%d sample_cnt=%zu\n", |  | ||||||
| 					ENDPOINT_NUMBER(endp), delta, state->sample_cnt); |  | ||||||
| 				state->sample_cnt = 0; |  | ||||||
| 				state->next_time = ts_no; |  | ||||||
| 			} else if (delta < 0) { |  | ||||||
| 				LOGP(DLMGCP, LOGL_NOTICE, |  | ||||||
| 				     "RTP time jumps backwards, delta = %d, " |  | ||||||
| 				     "discarding buffered samples\n", |  | ||||||
| 				     delta); |  | ||||||
| 				state->sample_cnt = 0; |  | ||||||
| 				state->sample_offs = 0; |  | ||||||
| 				return -EAGAIN; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			/* Make sure the samples start without offset */ |  | ||||||
| 			if (state->sample_offs && state->sample_cnt) |  | ||||||
| 				memmove(&state->samples[0], |  | ||||||
| 					&state->samples[state->sample_offs], |  | ||||||
| 					state->sample_cnt * |  | ||||||
| 					sizeof(state->samples[0])); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		state->sample_offs = 0; |  | ||||||
|  |  | ||||||
| 		/* Append decoded audio to samples */ |  | ||||||
| 		decode_audio(state, &src, &nbytes); |  | ||||||
|  |  | ||||||
| 		if (nbytes > 0) |  | ||||||
| 			LOGP(DLMGCP, LOGL_NOTICE, |  | ||||||
| 			     "Skipped audio frame in RTP packet: %zu octets\n", |  | ||||||
| 			     nbytes); |  | ||||||
| 	} else |  | ||||||
| 		ts_no = state->next_time; |  | ||||||
|  |  | ||||||
| 	if (state->sample_cnt < state->dst_packet_duration) |  | ||||||
| 		return -EAGAIN; |  | ||||||
|  |  | ||||||
| 	max_samples = |  | ||||||
| 		state->dst_packet_duration ? |  | ||||||
| 		state->dst_packet_duration : state->sample_cnt; |  | ||||||
|  |  | ||||||
| 	nsamples = state->sample_cnt; |  | ||||||
|  |  | ||||||
| 	rc = encode_audio(state, dst, buf_size, max_samples); |  | ||||||
| 	/* |  | ||||||
| 	 * There were no samples to encode? |  | ||||||
| 	 * TODO: how does this work for comfort noise? |  | ||||||
| 	 */ |  | ||||||
| 	if (rc == 0) |  | ||||||
| 		return -ENOMSG; |  | ||||||
| 	/* Any other error during the encoding */ |  | ||||||
| 	if (rc < 0) |  | ||||||
| 		return rc; |  | ||||||
|  |  | ||||||
| 	nsamples -= state->sample_cnt; |  | ||||||
|  |  | ||||||
| 	*len = rtp_hdr_size + rc; |  | ||||||
| 	rtp_hdr->sequence = htons(state->next_seq); |  | ||||||
| 	rtp_hdr->timestamp = htonl(ts_no); |  | ||||||
|  |  | ||||||
| 	state->next_seq += 1; |  | ||||||
| 	state->next_time = ts_no + nsamples; |  | ||||||
|  |  | ||||||
| 	/* |  | ||||||
| 	 * XXX: At this point we should always have consumed |  | ||||||
| 	 * samples. So doing OSMO_ASSERT(nsamples > 0) and returning |  | ||||||
| 	 * rtp_hdr_size should be fine. |  | ||||||
| 	 */ |  | ||||||
| 	return nsamples ? rtp_hdr_size : 0; |  | ||||||
| } |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| AM_CPPFLAGS = \ | AM_CPPFLAGS = \ | ||||||
| 	$(all_includes) \ | 	$(all_includes) \ | ||||||
| 	-I$(top_srcdir)/include \ | 	-I$(top_srcdir)/include \ | ||||||
|  | 	-I$(top_builddir)/include \ | ||||||
| 	-I$(top_builddir) \ | 	-I$(top_builddir) \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
| @@ -8,21 +9,19 @@ AM_CFLAGS = \ | |||||||
| 	-Wall \ | 	-Wall \ | ||||||
| 	$(LIBOSMOCORE_CFLAGS) \ | 	$(LIBOSMOCORE_CFLAGS) \ | ||||||
| 	$(LIBOSMOVTY_CFLAGS) \ | 	$(LIBOSMOVTY_CFLAGS) \ | ||||||
| 	$(LIBOSMONETIF_CFLAGS) \ | 	$(LIBOSMOABIS_CFLAGS) \ | ||||||
| 	$(COVERAGE_CFLAGS) \ | 	$(COVERAGE_CFLAGS) \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
| AM_LDFLAGS = \ | AM_LDFLAGS = \ | ||||||
| 	$(LIBOSMOCORE_LIBS) \ | 	$(LIBOSMOCORE_LIBS) \ | ||||||
| 	$(LIBOSMOGSM_LIBS) \ | 	$(LIBOSMOVTY_LIBS) \ | ||||||
| 	$(LIBOSMONETIF_LIBS) \ |  | ||||||
| 	$(COVERAGE_LDFLAGS) \ | 	$(COVERAGE_LDFLAGS) \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
| # This is _NOT_ the library release version, it's an API version. | # This is not at all related to the release version, but a range of supported | ||||||
| # Please read Chapter 6 "Library interface versions" of the libtool | # API versions. Read TODO_RELEASE in the source tree's root! | ||||||
| # documentation before making any modification | MGCP_CLIENT_LIBVERSION=9:0:0 | ||||||
| MGCP_CLIENT_LIBVERSION=1:0:1 |  | ||||||
|  |  | ||||||
| lib_LTLIBRARIES = \ | lib_LTLIBRARIES = \ | ||||||
| 	libosmo-mgcp-client.la \ | 	libosmo-mgcp-client.la \ | ||||||
| @@ -31,7 +30,9 @@ lib_LTLIBRARIES = \ | |||||||
| libosmo_mgcp_client_la_SOURCES = \ | libosmo_mgcp_client_la_SOURCES = \ | ||||||
| 	mgcp_client.c \ | 	mgcp_client.c \ | ||||||
| 	mgcp_client_vty.c \ | 	mgcp_client_vty.c \ | ||||||
| 	../libosmo-legacy-mgcp/mgcp_common.c \ | 	mgcp_client_fsm.c \ | ||||||
|  | 	mgcp_client_endpoint_fsm.c \ | ||||||
|  | 	mgcp_client_pool.c \ | ||||||
| 	$(NULL) | 	$(NULL) | ||||||
|  |  | ||||||
| libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION) | libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION) | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1036
									
								
								src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1036
									
								
								src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										766
									
								
								src/libosmo-mgcp-client/mgcp_client_fsm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										766
									
								
								src/libosmo-mgcp-client/mgcp_client_fsm.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,766 @@ | |||||||
|  | /* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * Author: Philipp Maier | ||||||
|  |  * | ||||||
|  |  * 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, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <osmocom/mgcp_client/mgcp_client.h> | ||||||
|  | #include <osmocom/mgcp_client/mgcp_client_fsm.h> | ||||||
|  | #include <osmocom/core/utils.h> | ||||||
|  | #include <osmocom/core/fsm.h> | ||||||
|  | #include <osmocom/core/byteswap.h> | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | #include <osmocom/core/logging.h> | ||||||
|  | #include <osmocom/core/sockaddr_str.h> | ||||||
|  |  | ||||||
|  | /* Context information, this is attached to the priv pointer of the FSM and | ||||||
|  |  * is also handed back when dispatcheing events to the parent FSM. This is | ||||||
|  |  * purly intened and not meant to be accessible for the API user */ | ||||||
|  | struct mgcp_ctx { | ||||||
|  | 	/* MGCP client instance that is used to interact with the MGW */ | ||||||
|  | 	struct mgcp_client *mgcp; | ||||||
|  |  | ||||||
|  | 	/* The ID of the last pending transaction. This is used internally | ||||||
|  | 	 * to cancel the transaction in case of an error */ | ||||||
|  | 	mgcp_trans_id_t mgw_pending_trans; | ||||||
|  |  | ||||||
|  | 	/* Flag to mark that there is a pending transaction */ | ||||||
|  | 	bool mgw_trans_pending; | ||||||
|  |  | ||||||
|  | 	/* Connection ID which has been assigned by he MGW */ | ||||||
|  | 	char conn_id[MGCP_CONN_ID_MAXLEN]; | ||||||
|  |  | ||||||
|  | 	/* Local RTP connection info, the MGW will send outgoing traffic to the | ||||||
|  | 	 * ip/port specified here. The Address does not have to be choosen right | ||||||
|  | 	 * on the creation of a connection. It can always be modified later by | ||||||
|  | 	 * the user. */ | ||||||
|  | 	struct mgcp_conn_peer conn_peer_local; | ||||||
|  |  | ||||||
|  | 	/* Remote RTP connection info, the ip/port specified here is the address | ||||||
|  | 	 * where the MGW expects the RTP data to be sent. This address is | ||||||
|  | 	 * defined by soly by the MGW and can not be influenced by the user. */ | ||||||
|  | 	struct mgcp_conn_peer conn_peer_remote; | ||||||
|  |  | ||||||
|  | 	/* The terminate flag is a way to handle cornercase sitations that | ||||||
|  | 	 * might occur when the user runs into an error situation and sends | ||||||
|  | 	 * a DLCX command while the FSM is waiting for a response. In this | ||||||
|  | 	 * case the DLCX command is not executed immediately. Instead the | ||||||
|  | 	 * terminate flag is set. When the response to from the previous | ||||||
|  | 	 * operation is received, we know that there is a DLCX event is | ||||||
|  | 	 * pending. The FSM then generates the EV_DLCX by itsself before | ||||||
|  | 	 * it enters ST_READY to cause the immediate execution of the | ||||||
|  | 	 * DLCX procedure. (If normal operations are executed too fast, | ||||||
|  | 	 * the API functions will return an error. In general, the user | ||||||
|  | 	 * should synchronize using the callback events) */ | ||||||
|  | 	bool terminate; | ||||||
|  |  | ||||||
|  | 	/* Event that is sent when the current operation is completed (except | ||||||
|  | 	 * for DLCX, there the specified parent_term_evt is sent instead) */ | ||||||
|  | 	uint32_t parent_evt; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define S(x)	(1 << (x)) | ||||||
|  |  | ||||||
|  | #define MGCP_MGW_TIMEOUT 4	/* in seconds */ | ||||||
|  | #define MGCP_MGW_TIMEOUT_TIMER_NR 1 | ||||||
|  |  | ||||||
|  | enum fsm_mgcp_client_states { | ||||||
|  | 	ST_CRCX, | ||||||
|  | 	ST_CRCX_RESP, | ||||||
|  | 	ST_READY, | ||||||
|  | 	ST_MDCX_RESP, | ||||||
|  | 	ST_DLCX_RESP, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | enum fsm_mgcp_client_evt { | ||||||
|  | 	EV_CRCX, | ||||||
|  | 	EV_CRCX_RESP, | ||||||
|  | 	EV_MDCX, | ||||||
|  | 	EV_MDCX_RESP, | ||||||
|  | 	EV_DLCX, | ||||||
|  | 	EV_DLCX_RESP, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static const struct value_string fsm_mgcp_client_evt_names[] = { | ||||||
|  | 	OSMO_VALUE_STRING(EV_CRCX), | ||||||
|  | 	OSMO_VALUE_STRING(EV_CRCX_RESP), | ||||||
|  | 	OSMO_VALUE_STRING(EV_MDCX), | ||||||
|  | 	OSMO_VALUE_STRING(EV_MDCX_RESP), | ||||||
|  | 	OSMO_VALUE_STRING(EV_DLCX), | ||||||
|  | 	OSMO_VALUE_STRING(EV_DLCX_RESP), | ||||||
|  | 	{0, NULL} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void make_crcx_msg(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info) | ||||||
|  | { | ||||||
|  | 	*mgcp_msg = (struct mgcp_msg) { | ||||||
|  | 		.verb = MGCP_VERB_CRCX, | ||||||
|  | 		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | ||||||
|  | 			     | MGCP_MSG_PRESENCE_CONN_MODE), | ||||||
|  | 		.call_id = info->call_id, | ||||||
|  | 		.conn_mode = MGCP_CONN_RECV_ONLY, | ||||||
|  | 		.ptime = info->ptime, | ||||||
|  | 		.codecs_len = info->codecs_len, | ||||||
|  | 		.ptmap_len = info->ptmap_len, | ||||||
|  | 		.param_present = info->param_present | ||||||
|  | 	}; | ||||||
|  | 	osmo_strlcpy(mgcp_msg->endpoint, info->endpoint, MGCP_ENDPOINT_MAXLEN); | ||||||
|  | 	memcpy(mgcp_msg->codecs, info->codecs, sizeof(mgcp_msg->codecs)); | ||||||
|  | 	memcpy(mgcp_msg->ptmap, info->ptmap, sizeof(mgcp_msg->ptmap)); | ||||||
|  | 	memcpy(&mgcp_msg->param, &info->param, sizeof(mgcp_msg->param)); | ||||||
|  |  | ||||||
|  | 	if (info->x_osmo_ign) { | ||||||
|  | 		mgcp_msg->x_osmo_ign = info->x_osmo_ign; | ||||||
|  | 		mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_IGN; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (info->x_osmo_osmux_use) { | ||||||
|  | 		mgcp_msg->x_osmo_osmux_cid = info->x_osmo_osmux_cid; | ||||||
|  | 		mgcp_msg->presence |= MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void add_audio(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *info) | ||||||
|  | { | ||||||
|  | 	mgcp_msg->presence |= MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT; | ||||||
|  | 	mgcp_msg->audio_ip = info->addr; | ||||||
|  | 	mgcp_msg->audio_port = info->port; | ||||||
|  | 	mgcp_msg->conn_mode = MGCP_CONN_RECV_SEND; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void set_conn_mode(struct mgcp_msg *mgcp_msg, struct mgcp_conn_peer *peer) | ||||||
|  | { | ||||||
|  | 	enum mgcp_connection_mode conn_mode = peer->conn_mode; | ||||||
|  | 	if (conn_mode != MGCP_CONN_NONE) | ||||||
|  | 		mgcp_msg->conn_mode = conn_mode; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct msgb *make_mdcx_msg(struct mgcp_ctx *mgcp_ctx) | ||||||
|  | { | ||||||
|  | 	struct mgcp_msg mgcp_msg; | ||||||
|  |  | ||||||
|  | 	mgcp_msg = (struct mgcp_msg) { | ||||||
|  | 		.verb = MGCP_VERB_MDCX, | ||||||
|  | 		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID | | ||||||
|  | 			     MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT), | ||||||
|  | 		.call_id =  mgcp_ctx->conn_peer_remote.call_id, | ||||||
|  | 		.conn_id = mgcp_ctx->conn_id, | ||||||
|  | 		.conn_mode = MGCP_CONN_RECV_SEND, | ||||||
|  | 		.audio_ip = mgcp_ctx->conn_peer_local.addr, | ||||||
|  | 		.audio_port = mgcp_ctx->conn_peer_local.port, | ||||||
|  | 		.ptime = mgcp_ctx->conn_peer_local.ptime, | ||||||
|  | 		.codecs_len = mgcp_ctx->conn_peer_local.codecs_len, | ||||||
|  | 		.ptmap_len = mgcp_ctx->conn_peer_local.ptmap_len, | ||||||
|  | 		.param_present = mgcp_ctx->conn_peer_local.param_present | ||||||
|  | 	}; | ||||||
|  | 	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN); | ||||||
|  | 	memcpy(mgcp_msg.codecs, mgcp_ctx->conn_peer_local.codecs, sizeof(mgcp_msg.codecs)); | ||||||
|  | 	memcpy(mgcp_msg.ptmap, mgcp_ctx->conn_peer_local.ptmap, sizeof(mgcp_msg.ptmap)); | ||||||
|  | 	memcpy(&mgcp_msg.param, &mgcp_ctx->conn_peer_local.param, sizeof(mgcp_ctx->conn_peer_local.param)); | ||||||
|  |  | ||||||
|  | 	set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local); | ||||||
|  |  | ||||||
|  | 	if (mgcp_ctx->conn_peer_local.x_osmo_osmux_use) { | ||||||
|  | 		mgcp_msg.x_osmo_osmux_cid = mgcp_ctx->conn_peer_local.x_osmo_osmux_cid; | ||||||
|  | 		mgcp_msg.presence |= MGCP_MSG_PRESENCE_X_OSMO_OSMUX_CID; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Note: We take the endpoint and the call_id from the remote | ||||||
|  | 	 * connection info, because we can be confident that the | ||||||
|  | 	 * information there is valid. For the local info, we explicitly | ||||||
|  | 	 * allow endpoint and call_id to be optional */ | ||||||
|  | 	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct msgb *make_dlcx_msg(struct mgcp_ctx *mgcp_ctx) | ||||||
|  | { | ||||||
|  | 	struct mgcp_msg mgcp_msg; | ||||||
|  |  | ||||||
|  | 	mgcp_msg = (struct mgcp_msg) { | ||||||
|  | 		.verb = MGCP_VERB_DLCX, | ||||||
|  | 		.presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID), | ||||||
|  | 		.call_id = mgcp_ctx->conn_peer_remote.call_id, | ||||||
|  | 		.conn_id = mgcp_ctx->conn_id, | ||||||
|  | 	}; | ||||||
|  | 	osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->conn_peer_remote.endpoint, MGCP_ENDPOINT_MAXLEN); | ||||||
|  |  | ||||||
|  | 	return mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv); | ||||||
|  |  | ||||||
|  | static void fsm_crcx_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||||
|  | { | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx = data; | ||||||
|  | 	struct mgcp_client *mgcp; | ||||||
|  | 	struct mgcp_msg mgcp_msg; | ||||||
|  | 	struct msgb *msg; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  | 	mgcp = mgcp_ctx->mgcp; | ||||||
|  | 	OSMO_ASSERT(mgcp); | ||||||
|  |  | ||||||
|  | 	switch (event) { | ||||||
|  | 	case EV_CRCX: | ||||||
|  | 		LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: creating connection on MGW endpoint:%s...\n", | ||||||
|  | 			 mgcp_ctx->conn_peer_local.endpoint); | ||||||
|  |  | ||||||
|  | 		make_crcx_msg(&mgcp_msg, &mgcp_ctx->conn_peer_local); | ||||||
|  | 		if (mgcp_ctx->conn_peer_local.port) | ||||||
|  | 			add_audio(&mgcp_msg, &mgcp_ctx->conn_peer_local); | ||||||
|  | 		set_conn_mode(&mgcp_msg, &mgcp_ctx->conn_peer_local); | ||||||
|  |  | ||||||
|  | 		msg = mgcp_msg_gen(mgcp_ctx->mgcp, &mgcp_msg); | ||||||
|  | 		OSMO_ASSERT(msg); | ||||||
|  |  | ||||||
|  | 		mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); | ||||||
|  | 		mgcp_ctx->mgw_trans_pending = true; | ||||||
|  | 		rc = mgcp_client_tx(mgcp, msg, mgw_crcx_resp_cb, fi); | ||||||
|  | 		if (rc < 0) { | ||||||
|  | 			osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		osmo_fsm_inst_state_chg(fi, ST_CRCX_RESP, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		OSMO_ASSERT(false); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Return the CI that the MGW allocated during CRCX response. This is purely informational for logging | ||||||
|  |  * and identity tracking; the mgcp_conn_*() functions take care of using the right CI internally. */ | ||||||
|  | const char *mgcp_conn_get_ci(struct osmo_fsm_inst *fi) | ||||||
|  | { | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||||
|  | 	return mgcp_ctx->conn_id; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Get the mgcp_client that is used with this mgcp_client_fsm instance */ | ||||||
|  | struct mgcp_client *mgcp_conn_get_client(struct osmo_fsm_inst *fi) | ||||||
|  | { | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx; | ||||||
|  |  | ||||||
|  | 	if (!fi) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	mgcp_ctx = fi->priv; | ||||||
|  | 	return mgcp_ctx->mgcp; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void mgw_crcx_resp_cb(struct mgcp_response *r, void *priv) | ||||||
|  | { | ||||||
|  | 	struct osmo_fsm_inst *fi = priv; | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(fi); | ||||||
|  | 	mgcp_ctx = fi->priv; | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  |  | ||||||
|  | 	mgcp_ctx->mgw_trans_pending = false; | ||||||
|  |  | ||||||
|  | 	if (r->head.response_code != 200) { | ||||||
|  | 		LOGPFSML(fi, LOGL_ERROR, | ||||||
|  | 			 "MGW/CRCX: response yields error: %d %s\n", r->head.response_code, r->head.comment); | ||||||
|  | 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	osmo_strlcpy(mgcp_ctx->conn_id, r->head.conn_id, sizeof(mgcp_ctx->conn_id)); | ||||||
|  | 	LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with CI: %s\n", mgcp_ctx->conn_id); | ||||||
|  |  | ||||||
|  | 	rc = mgcp_response_parse_params(r); | ||||||
|  | 	if (rc) { | ||||||
|  | 		LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: Cannot parse CRCX response\n"); | ||||||
|  | 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port); | ||||||
|  | 	if (r->head.x_osmo_osmux_use) { | ||||||
|  | 		LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded using Osmux %u\n", r->head.x_osmo_osmux_cid); | ||||||
|  | 		mgcp_ctx->conn_peer_remote.x_osmo_osmux_use = true; | ||||||
|  | 		mgcp_ctx->conn_peer_remote.x_osmo_osmux_cid = r->head.x_osmo_osmux_cid; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr)); | ||||||
|  | 	mgcp_ctx->conn_peer_remote.port = r->audio_port; | ||||||
|  |  | ||||||
|  | 	if (strlen(r->head.endpoint) > 0) { | ||||||
|  | 		/* If we get an endpoint identifier back from the MGW, take it */ | ||||||
|  | 		osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, r->head.endpoint, | ||||||
|  | 			     sizeof(mgcp_ctx->conn_peer_remote.endpoint)); | ||||||
|  | 	} else if (strstr(mgcp_ctx->conn_peer_local.endpoint, "*") == NULL) { | ||||||
|  | 		/* If we do not get an endpoint identifier back and the | ||||||
|  | 		 * identifier we used to create the connection is not a | ||||||
|  | 		 * wildcarded one, we take the local endpoint identifier | ||||||
|  | 		 * instead */ | ||||||
|  | 		osmo_strlcpy(mgcp_ctx->conn_peer_remote.endpoint, mgcp_ctx->conn_peer_local.endpoint, | ||||||
|  | 			     sizeof(mgcp_ctx->conn_peer_local.endpoint)); | ||||||
|  | 	} else { | ||||||
|  | 		LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: CRCX yielded not suitable endpoint identifier\n"); | ||||||
|  | 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mgcp_ctx->conn_peer_remote.call_id = mgcp_ctx->conn_peer_local.call_id; | ||||||
|  |  | ||||||
|  | 	osmo_fsm_inst_dispatch(fi, EV_CRCX_RESP, mgcp_ctx); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fsm_crcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||||
|  | { | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx = data; | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  |  | ||||||
|  | 	switch (event) { | ||||||
|  | 	case EV_CRCX_RESP: | ||||||
|  | 		osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0); | ||||||
|  | 		if (mgcp_ctx->terminate) { | ||||||
|  | 			/* Trigger immediate DLCX if DLCX was requested while the FSM was | ||||||
|  | 			 * busy with the previous operation */ | ||||||
|  | 			LOGPFSML(fi, LOGL_ERROR, "MGW/CRCX: FSM was busy while DLCX was requested, executing now...\n"); | ||||||
|  | 			osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); | ||||||
|  | 		} else | ||||||
|  | 			osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		OSMO_ASSERT(false); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv); | ||||||
|  | static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv); | ||||||
|  |  | ||||||
|  | static void fsm_ready_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||||
|  | { | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx = data; | ||||||
|  | 	struct msgb *msg; | ||||||
|  | 	struct mgcp_client *mgcp; | ||||||
|  | 	uint32_t new_state; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  | 	mgcp = mgcp_ctx->mgcp; | ||||||
|  | 	OSMO_ASSERT(mgcp); | ||||||
|  |  | ||||||
|  | 	switch (event) { | ||||||
|  | 	case EV_MDCX: | ||||||
|  | 		msg = make_mdcx_msg(mgcp_ctx); | ||||||
|  | 		OSMO_ASSERT(msg); | ||||||
|  | 		rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_resp_cb, fi); | ||||||
|  | 		new_state = ST_MDCX_RESP; | ||||||
|  | 		break; | ||||||
|  | 	case EV_DLCX: | ||||||
|  | 		msg = make_dlcx_msg(mgcp_ctx); | ||||||
|  | 		OSMO_ASSERT(msg); | ||||||
|  | 		rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_resp_cb, fi); | ||||||
|  | 		new_state = ST_DLCX_RESP; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		OSMO_ASSERT(false); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg); | ||||||
|  | 	mgcp_ctx->mgw_trans_pending = true; | ||||||
|  |  | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	osmo_fsm_inst_state_chg(fi, new_state, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void mgw_mdcx_resp_cb(struct mgcp_response *r, void *priv) | ||||||
|  | { | ||||||
|  | 	struct osmo_fsm_inst *fi = priv; | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(fi); | ||||||
|  | 	mgcp_ctx = fi->priv; | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  |  | ||||||
|  | 	mgcp_ctx->mgw_trans_pending = false; | ||||||
|  |  | ||||||
|  | 	if (r->head.response_code != 200) { | ||||||
|  | 		LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: response yields error: %d %s\n", r->head.response_code, | ||||||
|  | 			 r->head.comment); | ||||||
|  | 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	rc = mgcp_response_parse_params(r); | ||||||
|  | 	if (rc) { | ||||||
|  | 		LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: Cannot parse MDCX response\n"); | ||||||
|  | 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	LOGPFSML(fi, LOGL_DEBUG, "MGW/MDCX: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port); | ||||||
|  |  | ||||||
|  | 	if (r->head.x_osmo_osmux_use) { | ||||||
|  | 		LOGPFSML(fi, LOGL_DEBUG, "MGW/CRCX: MGW responded using Osmux %u\n", r->head.x_osmo_osmux_cid); | ||||||
|  | 		mgcp_ctx->conn_peer_remote.x_osmo_osmux_use = true; | ||||||
|  | 		mgcp_ctx->conn_peer_remote.x_osmo_osmux_cid = r->head.x_osmo_osmux_cid; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	osmo_strlcpy(mgcp_ctx->conn_peer_remote.addr, r->audio_ip, sizeof(mgcp_ctx->conn_peer_remote.addr)); | ||||||
|  | 	mgcp_ctx->conn_peer_remote.port = r->audio_port; | ||||||
|  |  | ||||||
|  | 	osmo_fsm_inst_dispatch(fi, EV_MDCX_RESP, mgcp_ctx); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fsm_mdcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||||
|  | { | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx = data; | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  |  | ||||||
|  | 	switch (event) { | ||||||
|  | 	case EV_MDCX_RESP: | ||||||
|  | 		osmo_fsm_inst_state_chg(fi, ST_READY, 0, 0); | ||||||
|  | 		if (mgcp_ctx->terminate) { | ||||||
|  | 			/* Trigger immediate DLCX if DLCX was requested while the FSM was | ||||||
|  | 			 * busy with the previous operation */ | ||||||
|  | 			LOGPFSML(fi, LOGL_ERROR, "MGW/MDCX: FSM was busy while DLCX was requested, executing now...\n"); | ||||||
|  | 			osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); | ||||||
|  | 		} else | ||||||
|  | 			osmo_fsm_inst_dispatch(fi->proc.parent, mgcp_ctx->parent_evt, &mgcp_ctx->conn_peer_remote); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		OSMO_ASSERT(false); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void mgw_dlcx_resp_cb(struct mgcp_response *r, void *priv) | ||||||
|  | { | ||||||
|  | 	struct osmo_fsm_inst *fi = priv; | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(fi); | ||||||
|  | 	mgcp_ctx = fi->priv; | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  |  | ||||||
|  | 	mgcp_ctx->mgw_trans_pending = false; | ||||||
|  |  | ||||||
|  | 	if (r->head.response_code != 250) { | ||||||
|  | 		LOGPFSML(fi, LOGL_ERROR, | ||||||
|  | 			 "MGW/DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment); | ||||||
|  | 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	osmo_fsm_inst_dispatch(fi, EV_DLCX_RESP, mgcp_ctx); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fsm_dlcx_resp_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data) | ||||||
|  | { | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx = data; | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  |  | ||||||
|  | 	switch (event) { | ||||||
|  | 	case EV_DLCX_RESP: | ||||||
|  | 		/* Rub out the connection identifier, since the connection | ||||||
|  | 		 * is no longer present and we will use the connection id | ||||||
|  | 		 * to know in error cases if the connection is still present | ||||||
|  | 		 * or not */ | ||||||
|  | 		memset(mgcp_ctx->conn_id, 0, sizeof(mgcp_ctx->conn_id)); | ||||||
|  |  | ||||||
|  | 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		OSMO_ASSERT(false); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int fsm_timeout_cb(struct osmo_fsm_inst *fi) | ||||||
|  | { | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||||
|  | 	struct mgcp_client *mgcp; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  | 	mgcp = mgcp_ctx->mgcp; | ||||||
|  | 	OSMO_ASSERT(mgcp); | ||||||
|  |  | ||||||
|  | 	if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) { | ||||||
|  | 		/* Note: We were unable to communicate with the MGW, | ||||||
|  | 		 * unfortunately there is no meaningful action we can take | ||||||
|  | 		 * now other than giving up. */ | ||||||
|  | 		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); | ||||||
|  | 	} else { | ||||||
|  | 		/* Note: Ther must not be any unsolicited timers | ||||||
|  | 		 * in this FSM. If so, we have serious problem. */ | ||||||
|  | 		OSMO_ASSERT(false); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void fsm_cleanup_cb(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) | ||||||
|  | { | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||||
|  | 	struct mgcp_client *mgcp; | ||||||
|  | 	struct msgb *msg; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  | 	mgcp = mgcp_ctx->mgcp; | ||||||
|  | 	OSMO_ASSERT(mgcp); | ||||||
|  |  | ||||||
|  | 	/* If there is still a transaction pending, cancel it now. */ | ||||||
|  | 	if (mgcp_ctx->mgw_trans_pending) | ||||||
|  | 		mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans); | ||||||
|  |  | ||||||
|  | 	/* Should the FSM be terminated while there are still open connections | ||||||
|  | 	 * on the MGW, we send an unconditional DLCX to terminate the | ||||||
|  | 	 * connection. This is not the normal case. The user should always use | ||||||
|  | 	 * mgcp_conn_delete() to instruct the FSM to perform a graceful exit. | ||||||
|  | 	 * If in ST_DLCX_RESP, a DLCX was already sent and we did not get a | ||||||
|  | 	 * response. No point in sending another one. */ | ||||||
|  | 	if (fi->state != ST_DLCX_RESP && strlen(mgcp_ctx->conn_id)) { | ||||||
|  | 		LOGPFSML(fi, LOGL_INFO, "Conn cleanup, sending DLCX for %s %s\n", mgcp_ctx->conn_peer_remote.endpoint, | ||||||
|  | 			 mgcp_ctx->conn_id); | ||||||
|  | 		msg = make_dlcx_msg(mgcp_ctx); | ||||||
|  | 		if (!msg) | ||||||
|  | 			LOGPFSML(fi, LOGL_ERROR, "MGW/DLCX: Error composing DLCX message\n"); | ||||||
|  | 		else | ||||||
|  | 			mgcp_client_tx(mgcp, msg, NULL, NULL); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	talloc_free(mgcp_ctx); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static struct osmo_fsm_state fsm_mgcp_client_states[] = { | ||||||
|  |  | ||||||
|  | 	/* Initial CRCX state. This state is immediately entered and executed | ||||||
|  | 	 * when the FSM is started. The rationale is that we first have to | ||||||
|  | 	 * create a connectin before we can execute other operations on that | ||||||
|  | 	 * connection. */ | ||||||
|  | 	[ST_CRCX] = { | ||||||
|  | 		     .in_event_mask = S(EV_CRCX), | ||||||
|  | 		     .out_state_mask = S(ST_CRCX_RESP), | ||||||
|  | 		     .name = OSMO_STRINGIFY(ST_CRCX), | ||||||
|  | 		     .action = fsm_crcx_cb, | ||||||
|  | 		     }, | ||||||
|  |  | ||||||
|  | 	/* Wait for the response to a CRCX operation, check and process the | ||||||
|  | 	 * results, change to ST_READY afterwards. */ | ||||||
|  | 	[ST_CRCX_RESP] = { | ||||||
|  | 			  .in_event_mask = S(EV_CRCX_RESP), | ||||||
|  | 			  .out_state_mask = S(ST_READY), | ||||||
|  | 			  .name = OSMO_STRINGIFY(ST_CRCX_RESP), | ||||||
|  | 			  .action = fsm_crcx_resp_cb, | ||||||
|  | 			  }, | ||||||
|  |  | ||||||
|  | 	/* In this idle state we wait for further operations (e.g. MDCX) that | ||||||
|  | 	 * can be executed by the user using the API. There is no timeout in | ||||||
|  | 	 * this state. The connection lives on until the user decides to | ||||||
|  | 	 * terminate it (DLCX). */ | ||||||
|  | 	[ST_READY] = { | ||||||
|  | 		      .in_event_mask = S(EV_MDCX) | S(EV_DLCX), | ||||||
|  | 		      .out_state_mask = S(ST_MDCX_RESP) | S(ST_DLCX_RESP), | ||||||
|  | 		      .name = OSMO_STRINGIFY(ST_READY), | ||||||
|  | 		      .action = fsm_ready_cb, | ||||||
|  | 		      }, | ||||||
|  |  | ||||||
|  | 	/* Wait for the response of a MDCX operation, check and process the | ||||||
|  | 	 * results, change to ST_READY afterwards. */ | ||||||
|  | 	[ST_MDCX_RESP] = { | ||||||
|  | 			  .in_event_mask = S(EV_MDCX_RESP), | ||||||
|  | 			  .out_state_mask = S(ST_READY), | ||||||
|  | 			  .name = OSMO_STRINGIFY(ST_MDCX_RESP), | ||||||
|  | 			  .action = fsm_mdcx_resp_cb, | ||||||
|  | 			  }, | ||||||
|  |  | ||||||
|  | 	/* Wait for the response of a DLCX operation and terminate the FSM | ||||||
|  | 	 * normally. */ | ||||||
|  | 	[ST_DLCX_RESP] = { | ||||||
|  | 			  .in_event_mask = S(EV_DLCX_RESP), | ||||||
|  | 			  .out_state_mask = 0, | ||||||
|  | 			  .name = OSMO_STRINGIFY(ST_DLCX_RESP), | ||||||
|  | 			  .action = fsm_dlcx_resp_cb, | ||||||
|  | 			  }, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static struct osmo_fsm fsm_mgcp_client = { | ||||||
|  | 	.name = "MGCP_CONN", | ||||||
|  | 	.states = fsm_mgcp_client_states, | ||||||
|  | 	.num_states = ARRAY_SIZE(fsm_mgcp_client_states), | ||||||
|  | 	.timer_cb = fsm_timeout_cb, | ||||||
|  | 	.cleanup = fsm_cleanup_cb, | ||||||
|  | 	.event_names = fsm_mgcp_client_evt_names, | ||||||
|  | 	.log_subsys = DLMGCP, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /*! allocate FSM, and create a new connection on the MGW. | ||||||
|  |  *  \param[in] mgcp MGCP client descriptor. | ||||||
|  |  *  \param[in] parent_fi Parent FSM instance. | ||||||
|  |  *  \param[in] parent_term_evt Event to be sent to parent when terminating. | ||||||
|  |  *  \param[in] parent_evt Event to be sent to parent when operation is done. | ||||||
|  |  *  \param[in] conn_peer Connection parameters (ip, port...). | ||||||
|  |  *  \returns newly-allocated, initialized and registered FSM instance, NULL on error. */ | ||||||
|  | struct osmo_fsm_inst *mgcp_conn_create(struct mgcp_client *mgcp, struct osmo_fsm_inst *parent_fi, | ||||||
|  | 				       uint32_t parent_term_evt, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) | ||||||
|  | { | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx; | ||||||
|  | 	struct osmo_fsm_inst *fi; | ||||||
|  | 	struct in6_addr ip_test; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(parent_fi); | ||||||
|  | 	OSMO_ASSERT(mgcp); | ||||||
|  | 	OSMO_ASSERT(conn_peer); | ||||||
|  |  | ||||||
|  | 	/* Check if IP/Port information in conn info makes sense */ | ||||||
|  | 	if (conn_peer->port && inet_pton(osmo_ip_str_type(conn_peer->addr), | ||||||
|  | 					 conn_peer->addr, &ip_test) != 1) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	/* Allocate and configure a new fsm instance */ | ||||||
|  | 	fi = osmo_fsm_inst_alloc_child(&fsm_mgcp_client, parent_fi, parent_term_evt); | ||||||
|  | 	OSMO_ASSERT(fi); | ||||||
|  | 	mgcp_ctx = talloc_zero(fi, struct mgcp_ctx); | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  | 	mgcp_ctx->mgcp = mgcp; | ||||||
|  | 	mgcp_ctx->parent_evt = parent_evt; | ||||||
|  |  | ||||||
|  | 	memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local)); | ||||||
|  | 	fi->priv = mgcp_ctx; | ||||||
|  |  | ||||||
|  | 	/* start state machine */ | ||||||
|  | 	OSMO_ASSERT(fi->state == ST_CRCX); | ||||||
|  | 	osmo_fsm_inst_dispatch(fi, EV_CRCX, mgcp_ctx); | ||||||
|  |  | ||||||
|  | 	return fi; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! modify an existing connection on the MGW. | ||||||
|  |  *  \param[in] fi FSM instance. | ||||||
|  |  *  \param[in] parent_evt Event to be sent to parent when operation is done. | ||||||
|  |  *  \param[in] conn_peer New connection information (ip, port...). | ||||||
|  |  *  \returns 0 on success, -EINVAL on error. */ | ||||||
|  | int mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer) | ||||||
|  | { | ||||||
|  | 	OSMO_ASSERT(fi); | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||||
|  | 	struct in6_addr ip_test; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  | 	OSMO_ASSERT(conn_peer); | ||||||
|  |  | ||||||
|  | 	/* The user must not issue an MDCX before the CRCX has completed, | ||||||
|  | 	 * if this happens, it means that the parent FSM has overhead the | ||||||
|  | 	 * parent_evt (mandatory!) and executed the MDCX without even | ||||||
|  | 	 * waiting for the results. Another reason could be that the | ||||||
|  | 	 * parent FSM got messed up */ | ||||||
|  | 	OSMO_ASSERT(fi->state != ST_CRCX_RESP); | ||||||
|  |  | ||||||
|  | 	/* If the user tries to issue an MDCX while an DLCX operation is | ||||||
|  | 	 * pending, there must be a serious problem with the paren FSM. | ||||||
|  | 	 * Eeither the parent_term_evt (mandatory!) has been overheard, | ||||||
|  | 	 * or the parant FSM got messed so badly that it still assumes | ||||||
|  | 	 * a live connection although it as killed it. */ | ||||||
|  | 	OSMO_ASSERT(fi->state != ST_DLCX_RESP); | ||||||
|  |  | ||||||
|  | 	/* Check if IP/Port parameters make sense */ | ||||||
|  | 	if (conn_peer->port == 0) { | ||||||
|  | 		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, port == 0\n"); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 	if (inet_pton(osmo_ip_str_type(conn_peer->addr), conn_peer->addr, &ip_test) != 1) { | ||||||
|  | 		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address %s\n", conn_peer->addr); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/*! The user may supply an endpoint identifier in conn_peer. The | ||||||
|  | 	 *  identifier is then checked. This check is optional. Later steps do | ||||||
|  | 	 *  not depend on the endpoint identifier supplied here because it is | ||||||
|  | 	 *  already implicitly known from the CRCX phase. */ | ||||||
|  | 	if (strlen(conn_peer->endpoint) && strcmp(conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint)) { | ||||||
|  | 		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, endpoint mismatches: requested %s, should be %s\n", | ||||||
|  | 			 conn_peer->endpoint, mgcp_ctx->conn_peer_remote.endpoint); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/*! Note: The call-id is implicitly known from the previous CRCX and | ||||||
|  | 	 *  will not be checked even when it is set in conn_peer. */ | ||||||
|  |  | ||||||
|  | 	mgcp_ctx->parent_evt = parent_evt; | ||||||
|  | 	memcpy(&mgcp_ctx->conn_peer_local, conn_peer, sizeof(mgcp_ctx->conn_peer_local)); | ||||||
|  | 	osmo_fsm_inst_dispatch(fi, EV_MDCX, mgcp_ctx); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! delete existing connection on the MGW, destroy FSM afterwards. | ||||||
|  |  *  \param[in] fi FSM instance. */ | ||||||
|  | void mgcp_conn_delete(struct osmo_fsm_inst *fi) | ||||||
|  | { | ||||||
|  | 	OSMO_ASSERT(fi); | ||||||
|  | 	struct mgcp_ctx *mgcp_ctx = fi->priv; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(mgcp_ctx); | ||||||
|  |  | ||||||
|  | 	if (fi->proc.terminating) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	/* Unlink FSM from parent, set the struct mgcp_client as new talloc ctx. */ | ||||||
|  | 	osmo_fsm_inst_unlink_parent(fi, mgcp_ctx->mgcp); | ||||||
|  |  | ||||||
|  | 	/* An error situation where the parent FSM must be killed immediately | ||||||
|  | 	 * may lead into a situation where the DLCX can not be executed right | ||||||
|  | 	 * at that moment because the FSM is still busy with another operation. | ||||||
|  | 	 * In those cases we postpone the DLCX so that the FSM and the | ||||||
|  | 	 * connections on the MGW get cleaned up gracefully. */ | ||||||
|  | 	if (fi->state != ST_READY) { | ||||||
|  | 		LOGPFSML(fi, LOGL_ERROR, "MGW: operation still pending, DLCX will be postponed.\n"); | ||||||
|  | 		mgcp_ctx->terminate = true; | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	osmo_fsm_inst_dispatch(fi, EV_DLCX, mgcp_ctx); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info) | ||||||
|  | { | ||||||
|  | 	/* I'd be fine with a smaller buffer and accept truncation, but gcc possibly refuses to build if | ||||||
|  | 	 * this buffer is too small. */ | ||||||
|  | 	static char buf[1024]; | ||||||
|  |  | ||||||
|  | 	if (!info) | ||||||
|  | 		return "NULL"; | ||||||
|  |  | ||||||
|  | 	if (info->endpoint[0] | ||||||
|  | 	    && info->addr[0]) | ||||||
|  | 		snprintf(buf, sizeof(buf), "%s:%s:%u", | ||||||
|  | 			 info->endpoint, info->addr, info->port); | ||||||
|  | 	else if (info->endpoint[0]) | ||||||
|  | 		snprintf(buf, sizeof(buf), "%s", info->endpoint); | ||||||
|  | 	else if (info->addr[0]) | ||||||
|  | 		snprintf(buf, sizeof(buf), "%s:%u", info->addr, info->port); | ||||||
|  | 	else | ||||||
|  | 		return "empty"; | ||||||
|  | 	return buf; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static __attribute__((constructor)) void osmo_mgcp_client_fsm_init() | ||||||
|  | { | ||||||
|  | 	OSMO_ASSERT(osmo_fsm_register(&fsm_mgcp_client) == 0); | ||||||
|  | } | ||||||
							
								
								
									
										216
									
								
								src/libosmo-mgcp-client/mgcp_client_pool.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								src/libosmo-mgcp-client/mgcp_client_pool.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | |||||||
|  | /* (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * Author: Philipp Maier | ||||||
|  |  * | ||||||
|  |  * 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, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <osmocom/mgcp_client/mgcp_client.h> | ||||||
|  | #include <osmocom/mgcp_client/mgcp_client_internal.h> | ||||||
|  | #include <osmocom/mgcp_client/mgcp_client_pool_internal.h> | ||||||
|  | #include <osmocom/mgcp_client/mgcp_client_pool.h> | ||||||
|  | #include <stddef.h> | ||||||
|  |  | ||||||
|  | #define LOGPPMGW(pool_member, level, fmt, args...) \ | ||||||
|  | LOGP(DLMGCP, level, "MGW-pool(%s) " fmt, mgcp_client_pool_member_name(pool_member), ## args) | ||||||
|  |  | ||||||
|  | /* Get a human readable name for a given pool member. */ | ||||||
|  | const char *mgcp_client_pool_member_name(const struct mgcp_client_pool_member *pool_member) | ||||||
|  | { | ||||||
|  | 	const struct mgcp_client *mpcp_client; | ||||||
|  | 	struct mgcp_client mpcp_client_dummy; | ||||||
|  | 	static char name[512]; | ||||||
|  | 	const char *description; | ||||||
|  |  | ||||||
|  | 	if (!pool_member) | ||||||
|  | 		return "(null)"; | ||||||
|  |  | ||||||
|  | 	/* It is not guranteed that a pool_member has an MGCP client. The client may not yet be initialized or the | ||||||
|  | 	 * initalization may have been failed. In this case we will generate a dummy MGCP client to work with. */ | ||||||
|  | 	if (!pool_member->client) { | ||||||
|  | 		memcpy(&mpcp_client_dummy.actual, &pool_member->conf, sizeof(mpcp_client_dummy.actual)); | ||||||
|  | 		mpcp_client = &mpcp_client_dummy; | ||||||
|  | 	} else { | ||||||
|  | 		mpcp_client = pool_member->client; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	description = mgcp_client_name(mpcp_client); | ||||||
|  | 	snprintf(name, sizeof(name), "%d:%s", pool_member->nr, description); | ||||||
|  |  | ||||||
|  | 	return name; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Allocate MGCP client pool. This is called once on startup and before the pool is used with | ||||||
|  |  *  mgcp_client_pool_vty_init(). Since the pool is linked with the VTY it must exist througout the entire runtime. | ||||||
|  |  *  \param[in] talloc_ctx talloc context. */ | ||||||
|  | struct mgcp_client_pool *mgcp_client_pool_alloc(void *talloc_ctx) | ||||||
|  | { | ||||||
|  | 	struct mgcp_client_pool *pool; | ||||||
|  |  | ||||||
|  | 	pool = talloc_zero(talloc_ctx, struct mgcp_client_pool); | ||||||
|  | 	if (!pool) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	INIT_LLIST_HEAD(&pool->pool); | ||||||
|  |  | ||||||
|  | 	return pool; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Initialize and connect an mcgp client pool. | ||||||
|  |  *  \param[in,out] mgcp MGCP client pool descriptor. | ||||||
|  |  *  \returns number of successfully initialized pool members. */ | ||||||
|  | unsigned int mgcp_client_pool_connect(struct mgcp_client_pool *pool) | ||||||
|  | { | ||||||
|  | 	struct mgcp_client_pool_member *pool_member; | ||||||
|  | 	unsigned int pool_members_initialized = 0; | ||||||
|  |  | ||||||
|  | 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||||
|  |  | ||||||
|  | 		/* Initialize client */ | ||||||
|  | 		pool_member->client = mgcp_client_init(pool_member, &pool_member->conf); | ||||||
|  | 		if (!pool_member->client) { | ||||||
|  | 			LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client initialization failed\n"); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* Set backpointer so that we can detect later that this MGCP client is managed | ||||||
|  | 		 * by this pool. */ | ||||||
|  | 		pool_member->client->pool = pool; | ||||||
|  |  | ||||||
|  | 		/* Connect client */ | ||||||
|  | 		if (mgcp_client_connect2(pool_member->client, 0)) { | ||||||
|  | 			LOGPPMGW(pool_member, LOGL_ERROR, "MGCP client connect failed at (%s:%u)\n", | ||||||
|  | 				 pool_member->conf.remote_addr, pool_member->conf.remote_port); | ||||||
|  | 			talloc_free(pool_member->client); | ||||||
|  | 			pool_member->client = NULL; | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		pool_members_initialized++; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return pool_members_initialized; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! register a single mgcp_client instance to the pool. | ||||||
|  |  *  \param[out] pool MGCP client pool descriptor. | ||||||
|  |  *  \param[in] mgcp MGCP client descriptor. */ | ||||||
|  | void mgcp_client_pool_register_single(struct mgcp_client_pool *pool, struct mgcp_client *mgcp_client) | ||||||
|  | { | ||||||
|  | 	/*! Some applications still support the non-pooled MGW VTY configuration variant provided by | ||||||
|  | 	 *  mgcp_client_vty_init(). If this is the case the mgcp_client instance created by mgcp_client_init() | ||||||
|  | 	 *  can be registered here so that it will appear as if it were part of the pool. When the user actively | ||||||
|  | 	 *  configures MGW pool members, the MGCP client registered here will be ignored. (The registration of | ||||||
|  | 	 *  multiple singe mgcp_client instances is not possible.) */ | ||||||
|  | 	pool->mgcp_client_single = mgcp_client; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Not every pool member may have a functional MGCP client, we will run through the pool once until we meet a | ||||||
|  |  * pool member that is suitable (has a client, is not blocked, has a low load). */ | ||||||
|  | static struct mgcp_client_pool_member *mgcp_client_pool_pick(struct mgcp_client_pool *pool) | ||||||
|  | { | ||||||
|  | 	struct mgcp_client_pool_member *pool_member; | ||||||
|  | 	struct mgcp_client_pool_member *pool_member_picked = NULL; | ||||||
|  | 	unsigned int n_pool_members = llist_count(&pool->pool); | ||||||
|  |  | ||||||
|  | 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||||
|  | 		if (pool_member->blocked == false && pool_member->client) { | ||||||
|  | 			if (!pool_member_picked) | ||||||
|  | 				pool_member_picked = pool_member; | ||||||
|  | 			else if (pool_member_picked->refcount > pool_member->refcount) | ||||||
|  | 				pool_member_picked = pool_member; | ||||||
|  | 		} else { | ||||||
|  | 			LOGPPMGW(pool_member, LOGL_DEBUG, "MGW pool has %u members -- MGW %u is unusable\n", n_pool_members, | ||||||
|  | 				 pool_member->nr); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (pool_member_picked) { | ||||||
|  | 		LOGPPMGW(pool_member_picked, LOGL_DEBUG, "MGW pool has %u members -- using MGW %u (active calls: %u)\n", | ||||||
|  | 			 n_pool_members, pool_member_picked->nr, pool_member_picked->refcount); | ||||||
|  | 		return pool_member_picked; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	LOGP(DLMGCP, LOGL_ERROR, | ||||||
|  | 	     "MGW pool has %u members, but no functional MGW pool member found -- check configuration!\n", | ||||||
|  | 	     n_pool_members); | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! get an MGCP client from the pool (increment reference counter). | ||||||
|  |  *  \param[in,out] pool MGCP client pool descriptor. | ||||||
|  |  *  \returns MGCP client descriptor, NULL if no member was found (empty pool). */ | ||||||
|  | struct mgcp_client *mgcp_client_pool_get(struct mgcp_client_pool *pool) | ||||||
|  | { | ||||||
|  | 	struct mgcp_client_pool_member *pool_member; | ||||||
|  |  | ||||||
|  | 	/*! When an MGCP client is taken from the pool it is still available for other calls. In fact only a reference | ||||||
|  | 	 *  counter is incremented to keep track on how many references to a specific MGCP client are currently used | ||||||
|  | 	 *  by the application code. */ | ||||||
|  |  | ||||||
|  | 	/* When the pool is empty, return a single MGCP client if it is registered. */ | ||||||
|  | 	if (llist_empty(&pool->pool) && pool->mgcp_client_single) { | ||||||
|  | 		LOGP(DLMGCP, LOGL_DEBUG, "MGW pool is empty -- using (single) MGW %s\n", | ||||||
|  | 		     mgcp_client_name(pool->mgcp_client_single)); | ||||||
|  | 		return pool->mgcp_client_single; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Abort when the pool is empty */ | ||||||
|  | 	if (llist_empty(&pool->pool)) { | ||||||
|  | 		LOGP(DLMGCP, LOGL_ERROR, "MGW pool is empty -- no MGW available!\n"); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Pick a suitable pool member */ | ||||||
|  | 	pool_member = mgcp_client_pool_pick(pool); | ||||||
|  | 	if (pool_member) { | ||||||
|  | 		pool_member->refcount++; | ||||||
|  | 		return pool_member->client; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! put an MGCP client back into the pool (decrement reference counter). | ||||||
|  |  *  \param[in,out] pool MGCP client pool descriptor. | ||||||
|  |  *  \param[in] mgcp MGCP client descriptor. | ||||||
|  |  * | ||||||
|  |  * This function is able to detect automatically to which pool the mgcp_client belongs. If the mgcp_client does | ||||||
|  |  * not belong to a pool at all, the function call will have no effect. */ | ||||||
|  | void mgcp_client_pool_put(struct mgcp_client *mgcp_client) | ||||||
|  | { | ||||||
|  | 	struct mgcp_client_pool_member *pool_member; | ||||||
|  | 	struct mgcp_client_pool *pool; | ||||||
|  |  | ||||||
|  | 	if (!mgcp_client) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	if (mgcp_client->pool) | ||||||
|  | 		pool = mgcp_client->pool; | ||||||
|  | 	else | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||||
|  | 		if (pool_member->client == mgcp_client) { | ||||||
|  | 			if (pool_member->refcount == 0) { | ||||||
|  | 				LOGPPMGW(pool_member, LOGL_ERROR, "MGW pool member has invalid refcount\n"); | ||||||
|  | 				return; | ||||||
|  | 			} | ||||||
|  | 			pool_member->refcount--; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| /* MGCPGW client interface to quagga VTY */ | /* MGCP client interface to quagga VTY */ | ||||||
| /* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> | /* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> | ||||||
|  * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c) |  * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c) | ||||||
|  * (C) 2009 by Harald Welte <laforge@gnumonks.org> |  * (C) 2009 by Harald Welte <laforge@gnumonks.org> | ||||||
| @@ -6,16 +6,16 @@ | |||||||
|  * All Rights Reserved |  * All Rights Reserved | ||||||
|  * |  * | ||||||
|  * This program is free software; you can redistribute it and/or modify |  * This program is free software; you can redistribute it and/or modify | ||||||
|  * it under the terms of the GNU Affero General Public License as published by |  * it under the terms of the GNU General Public License as published by | ||||||
|  * the Free Software Foundation; either version 3 of the License, or |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  * (at your option) any later version. |  * (at your option) any later version. | ||||||
|  * |  * | ||||||
|  * This program is distributed in the hope that it will be useful, |  * This program is distributed in the hope that it will be useful, | ||||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  * GNU Affero General Public License for more details. |  * GNU General Public License for more details. | ||||||
|  * |  * | ||||||
|  * You should have received a copy of the GNU Affero General Public License |  * You should have received a copy of the GNU General Public License | ||||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  * |  * | ||||||
|  */ |  */ | ||||||
| @@ -24,149 +24,534 @@ | |||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <talloc.h> | #include <talloc.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/vty/vty.h> | ||||||
| #include <osmocom/vty/command.h> | #include <osmocom/vty/command.h> | ||||||
|  | #include <osmocom/vty/misc.h> | ||||||
| #include <osmocom/core/utils.h> | #include <osmocom/core/utils.h> | ||||||
|  |  | ||||||
| #include <osmocom/legacy_mgcp/vty.h> |  | ||||||
| #include <osmocom/mgcp_client/mgcp_client.h> | #include <osmocom/mgcp_client/mgcp_client.h> | ||||||
|  | #include <osmocom/mgcp_client/mgcp_client_internal.h> | ||||||
|  | #include <osmocom/mgcp_client/mgcp_client_pool_internal.h> | ||||||
|  |  | ||||||
| #define MGCPGW_STR "MGCP gateway configuration for RTP streams\n" | #define MGW_STR MGCP_CLIENT_MGW_STR | ||||||
|  |  | ||||||
| void *global_mgcp_client_ctx = NULL; | /* Only common (non-pooled) VTY connands will use this talloc context. All | ||||||
| struct mgcp_client_conf *global_mgcp_client_conf = NULL; |  * pooled VTY commands will use the pool (global_mgcp_client_pool) as | ||||||
|  |  * talloc context. */ | ||||||
|  | static void *global_mgcp_client_ctx = NULL; | ||||||
|  |  | ||||||
| DEFUN(cfg_mgcpgw_local_ip, cfg_mgcpgw_local_ip_cmd, | /* MGCP Client configuration used with mgcp_client_vty_init(). (This pointer | ||||||
|       "mgcpgw local-ip A.B.C.D", |  * points to user provided memory, so it cannot be used as talloc context.) */ | ||||||
|       MGCPGW_STR "local bind to connect to MGCP gateway with\n" | static struct mgcp_client_conf *global_mgcp_client_conf = NULL; | ||||||
|       "local bind IP address\n") |  | ||||||
|  | /* Pointer to the MGCP pool that is managed by mgcp_client_pool_vty_init() */ | ||||||
|  | static struct mgcp_client_pool *global_mgcp_client_pool = NULL; | ||||||
|  |  | ||||||
|  | struct mgcp_client_conf *get_mgcp_client_config(struct vty *vty) | ||||||
| { | { | ||||||
| 	if (!global_mgcp_client_conf) | 	if (global_mgcp_client_pool && vty->node == global_mgcp_client_pool->vty_node->node) | ||||||
| 		return CMD_ERR_NOTHING_TODO; | 		return vty->index; | ||||||
| 	OSMO_ASSERT(global_mgcp_client_ctx); | 	else | ||||||
| 	global_mgcp_client_conf->local_addr = | 		return global_mgcp_client_conf; | ||||||
| 		talloc_strdup(global_mgcp_client_ctx, argv[0]); |  | ||||||
| 	return CMD_SUCCESS; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| DEFUN(cfg_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd, | DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd, | ||||||
|       "mgcpgw local-port <0-65535>", |       "mgw local-ip " VTY_IPV46_CMD, | ||||||
|       MGCPGW_STR "local bind to connect to MGCP gateway with\n" |       MGW_STR "local bind to connect to MGW from\n" | ||||||
|  |       "local bind IPv4 address\n" | ||||||
|  |       "local bind IPv6 address\n") | ||||||
|  | { | ||||||
|  | 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||||
|  |  | ||||||
|  | 	osmo_talloc_replace_string(global_mgcp_client_ctx, | ||||||
|  | 				   (char **)&conf->local_addr, | ||||||
|  | 				   argv[0]); | ||||||
|  | 	return CMD_SUCCESS; | ||||||
|  | } | ||||||
|  | ALIAS_DEPRECATED(cfg_mgw_local_ip, cfg_mgcpgw_local_ip_cmd, | ||||||
|  | 		 "mgcpgw local-ip A.B.C.D", | ||||||
|  | 		 MGW_STR "local bind to connect to MGCP gateway with\n" | ||||||
|  | 		 "local bind IP address\n") | ||||||
|  |  | ||||||
|  | DEFUN(cfg_mgw_local_port, cfg_mgw_local_port_cmd, | ||||||
|  |       "mgw local-port <0-65535>", | ||||||
|  |       MGW_STR "local port to connect to MGW from\n" | ||||||
|       "local bind port\n") |       "local bind port\n") | ||||||
| { | { | ||||||
| 	if (!global_mgcp_client_conf) | 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||||
| 		return CMD_ERR_NOTHING_TODO; |  | ||||||
| 	global_mgcp_client_conf->local_port = atoi(argv[0]); | 	conf->local_port = atoi(argv[0]); | ||||||
| 	return CMD_SUCCESS; | 	return CMD_SUCCESS; | ||||||
| } | } | ||||||
|  | ALIAS_DEPRECATED(cfg_mgw_local_port, cfg_mgcpgw_local_port_cmd, | ||||||
|  | 		 "mgcpgw local-port <0-65535>", | ||||||
|  | 		 MGW_STR "local bind to connect to MGCP gateway with\n" | ||||||
|  | 		 "local bind port\n") | ||||||
|  |  | ||||||
| DEFUN(cfg_mgcpgw_remote_ip, cfg_mgcpgw_remote_ip_cmd, | DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd, | ||||||
|  |       "mgw remote-ip " VTY_IPV46_CMD, | ||||||
|  |       MGW_STR "remote IP address to reach the MGW at\n" | ||||||
|  |       "remote IPv4 address\n" | ||||||
|  |       "remote IPv6 address\n") | ||||||
|  | { | ||||||
|  | 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||||
|  |  | ||||||
|  | 	osmo_talloc_replace_string(global_mgcp_client_ctx, | ||||||
|  | 				   (char **)&conf->remote_addr, argv[0]); | ||||||
|  | 	return CMD_SUCCESS; | ||||||
|  | } | ||||||
|  | ALIAS_DEPRECATED(cfg_mgw_remote_ip, cfg_mgcpgw_remote_ip_cmd, | ||||||
| 		 "mgcpgw remote-ip A.B.C.D", | 		 "mgcpgw remote-ip A.B.C.D", | ||||||
|       MGCPGW_STR "remote bind to connect to MGCP gateway with\n" | 		 MGW_STR "remote bind to connect to MGCP gateway with\n" | ||||||
| 		 "remote bind IP address\n") | 		 "remote bind IP address\n") | ||||||
|  |  | ||||||
|  | DEFUN(cfg_mgw_remote_port, cfg_mgw_remote_port_cmd, | ||||||
|  |       "mgw remote-port <0-65535>", | ||||||
|  |       MGW_STR "remote port to reach the MGW at\n" | ||||||
|  |       "remote port\n") | ||||||
| { | { | ||||||
| 	if (!global_mgcp_client_conf) | 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||||
| 		return CMD_ERR_NOTHING_TODO; |  | ||||||
| 	OSMO_ASSERT(global_mgcp_client_ctx); | 	conf->remote_port = atoi(argv[0]); | ||||||
| 	global_mgcp_client_conf->remote_addr = |  | ||||||
| 		talloc_strdup(global_mgcp_client_ctx, argv[0]); |  | ||||||
| 	return CMD_SUCCESS; | 	return CMD_SUCCESS; | ||||||
| } | } | ||||||
|  | ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd, | ||||||
| DEFUN(cfg_mgcpgw_remote_port, cfg_mgcpgw_remote_port_cmd, |  | ||||||
| 		 "mgcpgw remote-port <0-65535>", | 		 "mgcpgw remote-port <0-65535>", | ||||||
|       MGCPGW_STR "remote bind to connect to MGCP gateway with\n" | 		 MGW_STR "remote bind to connect to MGCP gateway with\n" | ||||||
| 		 "remote bind port\n") | 		 "remote bind port\n") | ||||||
| { |  | ||||||
| 	if (!global_mgcp_client_conf) |  | ||||||
| 		return CMD_ERR_NOTHING_TODO; |  | ||||||
| 	global_mgcp_client_conf->remote_port = atoi(argv[0]); |  | ||||||
| 	return CMD_SUCCESS; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| DEFUN(cfg_mgcpgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd, | DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd, | ||||||
|       "mgcpgw endpoint-range <1-65534> <1-65534>", |       "mgw endpoint-range <1-65534> <1-65534>", | ||||||
|       MGCPGW_STR "usable range of endpoint identifiers\n" |       MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n" | ||||||
|       "set first useable endpoint identifier\n" |       "-\n" "-\n") | ||||||
|       "set the last useable endpoint identifier\n") |  | ||||||
| { | { | ||||||
| 	uint16_t first_endpoint = atoi(argv[0]); | 	vty_out(vty, "Please do not use legacy config 'mgw endpoint-range'" | ||||||
| 	uint16_t last_endpoint = atoi(argv[1]); | 		" (the range can no longer be defined by the MGCP client)%s", | ||||||
|  |  | ||||||
| 	if (last_endpoint < first_endpoint) { |  | ||||||
| 		vty_out(vty, "last endpoint must be greater than first endpoint!%s", |  | ||||||
| 		VTY_NEWLINE); | 		VTY_NEWLINE); | ||||||
| 	return CMD_SUCCESS; | 	return CMD_SUCCESS; | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	global_mgcp_client_conf->first_endpoint = first_endpoint; |  | ||||||
| 	global_mgcp_client_conf->last_endpoint = last_endpoint; |  | ||||||
| 	return CMD_SUCCESS; |  | ||||||
| } | } | ||||||
|  | ALIAS_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd, | ||||||
|  |       "mgcpgw endpoint-range <1-65534> <1-65534>", | ||||||
|  |       MGW_STR "usable range of endpoint identifiers\n" | ||||||
|  |       "set first useable endpoint identifier\n" | ||||||
|  |       "set the last useable endpoint identifier\n") | ||||||
|  |  | ||||||
| #define BTS_START_STR "First UDP port allocated for the BTS side\n" | #define BTS_START_STR "First UDP port allocated for the BTS side\n" | ||||||
| #define UDP_PORT_STR "UDP Port number\n" | #define UDP_PORT_STR "UDP Port number\n" | ||||||
| DEFUN(cfg_mgcp_rtp_bts_base_port, | DEFUN_DEPRECATED(cfg_mgw_rtp_bts_base_port, | ||||||
|       cfg_mgcp_rtp_bts_base_port_cmd, |       cfg_mgw_rtp_bts_base_port_cmd, | ||||||
|  |       "mgw bts-base <0-65534>", | ||||||
|  |       MGW_STR | ||||||
|  |       "DEPRECATED: there is no explicit BTS side in current osmo-mgw\n" "-\n") | ||||||
|  | { | ||||||
|  | 	vty_out(vty, "Please do not use legacy config 'mgw bts-base'" | ||||||
|  | 		" (there is no explicit BTS side in an MGW anymore)%s", | ||||||
|  | 		VTY_NEWLINE); | ||||||
|  | 	return CMD_SUCCESS; | ||||||
|  | } | ||||||
|  | ALIAS_DEPRECATED(cfg_mgw_rtp_bts_base_port, | ||||||
|  |       cfg_mgcpgw_rtp_bts_base_port_cmd, | ||||||
|       "mgcpgw bts-base <0-65534>", |       "mgcpgw bts-base <0-65534>", | ||||||
|       MGCPGW_STR |       MGW_STR | ||||||
|       BTS_START_STR |       BTS_START_STR | ||||||
|       UDP_PORT_STR) |       UDP_PORT_STR) | ||||||
|  |  | ||||||
|  | DEFUN(cfg_mgw_endpoint_domain_name, | ||||||
|  |       cfg_mgw_endpoint_domain_name_cmd, | ||||||
|  |       "mgw endpoint-domain NAME", | ||||||
|  |       MGW_STR "Set the domain name to send in MGCP messages, e.g. the part 'foo' in 'rtpbridge/*@foo'.\n" | ||||||
|  |       "Domain name, should be alphanumeric.\n") | ||||||
| { | { | ||||||
| 	global_mgcp_client_conf->bts_base = atoi(argv[0]); | 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||||
|  |  | ||||||
|  | 	if (osmo_strlcpy(conf->endpoint_domain_name, argv[0], sizeof(conf->endpoint_domain_name)) | ||||||
|  | 	    >= sizeof(conf->endpoint_domain_name)) { | ||||||
|  | 		vty_out(vty, "%% Error: 'mgw endpoint-domain' name too long, max length is %zu: '%s'%s", | ||||||
|  | 			sizeof(conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE); | ||||||
|  | 		return CMD_WARNING; | ||||||
|  | 	} | ||||||
| 	return CMD_SUCCESS; | 	return CMD_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
| int mgcp_client_config_write(struct vty *vty, const char *indent) | DEFUN(cfg_mgw_reset_ep_name, | ||||||
|  |       cfg_mgw_reset_ep_name_cmd, | ||||||
|  |       "mgw reset-endpoint NAME", | ||||||
|  |       MGW_STR "Add an endpoint name that should be reset (DLCX) on connect to the reset-endpoint list," | ||||||
|  |       "e.g. 'rtpbridge/*'\n" | ||||||
|  |       "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n") | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  | 	struct reset_ep *reset_ep; | ||||||
|  | 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||||
|  |  | ||||||
|  | 	/* stop when the address is already in the list */ | ||||||
|  | 	llist_for_each_entry(reset_ep, &conf->reset_epnames, list) { | ||||||
|  | 		if (strcmp(argv[0], reset_ep->name) == 0) { | ||||||
|  | 			vty_out(vty, "%% duplicate endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||||
|  | 			return CMD_WARNING; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* the domain name is not part of the actual endpoint name */ | ||||||
|  | 	if (strchr(argv[0], '@')) { | ||||||
|  | 		vty_out(vty, "%% the endpoint name must be given without domain name ('%s')%s", | ||||||
|  | 			argv[0], VTY_NEWLINE); | ||||||
|  | 		return CMD_WARNING; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	reset_ep = talloc_zero(global_mgcp_client_ctx, struct reset_ep); | ||||||
|  | 	OSMO_ASSERT(reset_ep); | ||||||
|  |  | ||||||
|  | 	rc = osmo_strlcpy(reset_ep->name, argv[0], sizeof(reset_ep->name)); | ||||||
|  | 	if (rc >= sizeof(reset_ep->name)) { | ||||||
|  | 		vty_out(vty, "%% Error: 'mgw reset-endpoint' name too long, max length is %zu: '%s'%s", | ||||||
|  | 			sizeof(reset_ep->name) - 1, argv[0], VTY_NEWLINE); | ||||||
|  | 		talloc_free(reset_ep); | ||||||
|  | 		return CMD_WARNING; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	llist_add_tail(&reset_ep->list, &conf->reset_epnames); | ||||||
|  |  | ||||||
|  | 	return CMD_SUCCESS; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DEFUN(cfg_mgw_no_reset_ep_name, | ||||||
|  |       cfg_mgw_no_reset_ep_name_cmd, | ||||||
|  |       "no mgw reset-endpoint NAME", | ||||||
|  |       NO_STR MGW_STR "remove an endpoint name from the reset-endpoint list, e.g. 'rtpbridge/*'\n" | ||||||
|  |       "Endpoint name, e.g. 'rtpbridge/*' or 'ds/e1-0/s-3/su16-4'.\n") | ||||||
|  | { | ||||||
|  | 	struct reset_ep *reset_ep; | ||||||
|  | 	struct mgcp_client_conf *conf = get_mgcp_client_config(vty); | ||||||
|  |  | ||||||
|  | 	llist_for_each_entry(reset_ep, &conf->reset_epnames, list) { | ||||||
|  | 		if (strcmp(argv[0], reset_ep->name) == 0) { | ||||||
|  | 			llist_del(&reset_ep->list); | ||||||
|  | 			talloc_free(reset_ep); | ||||||
|  | 			return CMD_SUCCESS; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	vty_out(vty, "%% no such endpoint name configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||||
|  | 	return CMD_WARNING; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int config_write(struct vty *vty, const char *indent, struct mgcp_client_conf *conf) | ||||||
| { | { | ||||||
| 	const char *addr; | 	const char *addr; | ||||||
| 	int port; | 	int port; | ||||||
| 	uint16_t first_endpoint; | 	struct reset_ep *reset_ep; | ||||||
| 	uint16_t last_endpoint; |  | ||||||
| 	uint16_t bts_base; |  | ||||||
|  |  | ||||||
| 	addr = global_mgcp_client_conf->local_addr; | 	if (conf->description) | ||||||
|  | 		vty_out(vty, "%sdescription %s%s", indent, conf->description, VTY_NEWLINE); | ||||||
|  |  | ||||||
|  | 	addr = conf->local_addr; | ||||||
| 	if (addr) | 	if (addr) | ||||||
| 		vty_out(vty, "%smgcpgw local-ip %s%s", indent, addr, | 		vty_out(vty, "%smgw local-ip %s%s", indent, addr, | ||||||
| 			VTY_NEWLINE); | 			VTY_NEWLINE); | ||||||
| 	port = global_mgcp_client_conf->local_port; | 	port = conf->local_port; | ||||||
| 	if (port >= 0) | 	if (port >= 0) | ||||||
| 		vty_out(vty, "%smgcpgw local-port %u%s", indent, | 		vty_out(vty, "%smgw local-port %u%s", indent, | ||||||
| 			(uint16_t)port, VTY_NEWLINE); | 			(uint16_t)port, VTY_NEWLINE); | ||||||
|  |  | ||||||
| 	addr = global_mgcp_client_conf->remote_addr; | 	addr = conf->remote_addr; | ||||||
| 	if (addr) | 	if (addr) | ||||||
| 		vty_out(vty, "%smgcpgw remote-ip %s%s", indent, addr, | 		vty_out(vty, "%smgw remote-ip %s%s", indent, addr, | ||||||
| 			VTY_NEWLINE); | 			VTY_NEWLINE); | ||||||
| 	port = global_mgcp_client_conf->remote_port; | 	port = conf->remote_port; | ||||||
| 	if (port >= 0) | 	if (port >= 0) | ||||||
| 		vty_out(vty, "%smgcpgw remote-port %u%s", indent, | 		vty_out(vty, "%smgw remote-port %u%s", indent, | ||||||
| 			(uint16_t)port, VTY_NEWLINE); | 			(uint16_t)port, VTY_NEWLINE); | ||||||
|  |  | ||||||
| 	first_endpoint = global_mgcp_client_conf->first_endpoint; | 	if (conf->endpoint_domain_name[0]) | ||||||
| 	last_endpoint = global_mgcp_client_conf->last_endpoint; | 		vty_out(vty, "%smgw endpoint-domain %s%s", indent, | ||||||
| 	if (last_endpoint != 0) { | 			conf->endpoint_domain_name, VTY_NEWLINE); | ||||||
| 		vty_out(vty, "%smgcpgw endpoint-range %u %u%s", indent, |  | ||||||
| 			first_endpoint, last_endpoint, VTY_NEWLINE); | 	llist_for_each_entry(reset_ep, &conf->reset_epnames, list) | ||||||
|  | 		vty_out(vty, "%smgw reset-endpoint %s%s", indent, reset_ep->name, VTY_NEWLINE); | ||||||
|  |  | ||||||
|  | 	return CMD_SUCCESS; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Write out MGCP client config to VTY. | ||||||
|  |  *  \param[in] vty VTY to which we should print. | ||||||
|  |  *  \param[in] string used for indentation (e.g. " "). | ||||||
|  |  *  \returns CMD_SUCCESS on success, CMD_WARNING on error */ | ||||||
|  | int mgcp_client_config_write(struct vty *vty, const char *indent) | ||||||
|  | { | ||||||
|  | 	return config_write(vty, indent, global_mgcp_client_conf); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void vty_init_common(void *talloc_ctx, int node) | ||||||
|  | { | ||||||
|  | 	global_mgcp_client_ctx = talloc_ctx; | ||||||
|  |  | ||||||
|  | 	install_lib_element(node, &cfg_mgw_local_ip_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgw_local_port_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgw_remote_ip_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgw_remote_port_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgw_endpoint_range_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgw_rtp_bts_base_port_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgw_endpoint_domain_name_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgw_reset_ep_name_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgw_no_reset_ep_name_cmd); | ||||||
|  |  | ||||||
|  | 	osmo_fsm_vty_add_cmds(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Set up MGCP client VTY | ||||||
|  |  *  (called once at startup by the application process). | ||||||
|  |  *  \param[in] talloc_ctx talloc context to be used by the VTY for allocating memory. | ||||||
|  |  *  \param[in] node identifier of the node on which the VTY commands should be installed. | ||||||
|  |  *  \param[in] conf user provided memory to to store the MGCP client configuration data. */ | ||||||
|  | void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf) | ||||||
|  | { | ||||||
|  | 	global_mgcp_client_conf = conf; | ||||||
|  |  | ||||||
|  | 	/* deprecated 'mgcpgw' commands */ | ||||||
|  | 	install_lib_element(node, &cfg_mgcpgw_local_ip_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgcpgw_local_port_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgcpgw_remote_ip_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgcpgw_remote_port_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgcpgw_endpoint_range_cmd); | ||||||
|  | 	install_lib_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd); | ||||||
|  |  | ||||||
|  | 	vty_init_common(talloc_ctx, node); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int config_write_pool(struct vty *vty) | ||||||
|  | { | ||||||
|  | 	struct mgcp_client_pool *pool = global_mgcp_client_pool; | ||||||
|  | 	struct mgcp_client_pool_member *pool_member; | ||||||
|  | 	unsigned int indent_buf_len = strlen(pool->vty_indent) + 1 + 1; | ||||||
|  | 	char *indent = talloc_zero_size(vty, indent_buf_len); | ||||||
|  |  | ||||||
|  | 	snprintf(indent, indent_buf_len, "%s ", pool->vty_indent); | ||||||
|  |  | ||||||
|  | 	llist_for_each_entry(pool_member, &pool->pool, list) { | ||||||
|  | 		vty_out(vty, "%smgw %u%s", pool->vty_indent, pool_member->nr, VTY_NEWLINE); | ||||||
|  | 		config_write(vty, indent, &pool_member->conf); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	bts_base = global_mgcp_client_conf->bts_base; | 	talloc_free(indent); | ||||||
| 	if (bts_base) { | 	return CMD_SUCCESS; | ||||||
| 		vty_out(vty, "%smgcpgw bts-base %u%s", indent, | } | ||||||
| 			bts_base, VTY_NEWLINE); |  | ||||||
|  | /* Lookup the selected MGCP client config by its reference number */ | ||||||
|  | static struct mgcp_client_pool_member *pool_member_by_nr(unsigned int nr) | ||||||
|  | { | ||||||
|  | 	struct mgcp_client_pool_member *pool_member = NULL; | ||||||
|  | 	struct mgcp_client_pool_member *pool_member_tmp; | ||||||
|  |  | ||||||
|  | 	llist_for_each_entry(pool_member_tmp, &global_mgcp_client_pool->pool, list) { | ||||||
|  | 		if (pool_member_tmp->nr == nr) { | ||||||
|  | 			pool_member = pool_member_tmp; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return pool_member; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DEFUN_ATTR(cfg_mgw, | ||||||
|  | 	   cfg_mgw_cmd, "mgw <0-255>", "Select a MGCP client config to setup\n" "reference number\n", CMD_ATTR_IMMEDIATE) | ||||||
|  | { | ||||||
|  | 	int nr = atoi(argv[0]); | ||||||
|  | 	struct mgcp_client_pool_member *pool_member; | ||||||
|  |  | ||||||
|  | 	pool_member = pool_member_by_nr(nr); | ||||||
|  | 	if (!pool_member) { | ||||||
|  | 		pool_member = talloc_zero(global_mgcp_client_pool, struct mgcp_client_pool_member); | ||||||
|  | 		OSMO_ASSERT(pool_member); | ||||||
|  | 		mgcp_client_conf_init(&pool_member->conf); | ||||||
|  | 		pool_member->nr = nr; | ||||||
|  | 		llist_add_tail(&pool_member->list, &global_mgcp_client_pool->pool); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	vty->index = &pool_member->conf; | ||||||
|  | 	vty->index_sub = &pool_member->conf.description; | ||||||
|  | 	vty->node = global_mgcp_client_pool->vty_node->node; | ||||||
|  |  | ||||||
|  | 	return CMD_SUCCESS; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DEFUN_ATTR(cfg_no_mgw, | ||||||
|  | 	   cfg_no_mgw_cmd, | ||||||
|  | 	   "no mgw <0-255>", NO_STR "Select a MGCP client config to remove\n" "reference number\n", CMD_ATTR_IMMEDIATE) | ||||||
|  | { | ||||||
|  | 	int nr = atoi(argv[0]); | ||||||
|  | 	struct mgcp_client_pool_member *pool_member; | ||||||
|  |  | ||||||
|  | 	pool_member = pool_member_by_nr(nr); | ||||||
|  | 	if (!pool_member) { | ||||||
|  | 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||||
|  | 		return CMD_WARNING; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Make sure that there are no ongoing calls */ | ||||||
|  | 	if (pool_member->refcount > 0) { | ||||||
|  | 		vty_out(vty, "%% MGCP client (MGW %s) is still serving ongoing calls -- can't remove it now!%s", | ||||||
|  | 			mgcp_client_pool_member_name(pool_member), VTY_NEWLINE); | ||||||
|  | 		return CMD_WARNING; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	llist_del(&pool_member->list); | ||||||
|  | 	if (pool_member->client) { | ||||||
|  | 		mgcp_client_disconnect(pool_member->client); | ||||||
|  | 		talloc_free(pool_member->client); | ||||||
|  | 	} | ||||||
|  | 	talloc_free(pool_member); | ||||||
|  |  | ||||||
|  | 	return CMD_SUCCESS; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DEFUN_ATTR(mgw_reconnect, mgw_reconnect_cmd, | ||||||
|  | 	   "mgw <0-255> reconnect", | ||||||
|  | 	   MGW_STR "reference number\n" "reconfigure and reconnect MGCP client\n", CMD_ATTR_IMMEDIATE) | ||||||
|  | { | ||||||
|  | 	int nr = atoi(argv[0]); | ||||||
|  | 	struct mgcp_client_pool_member *pool_member = NULL; | ||||||
|  |  | ||||||
|  | 	pool_member = pool_member_by_nr(nr); | ||||||
|  | 	if (!pool_member) { | ||||||
|  | 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||||
|  | 		return CMD_WARNING; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Make sure that there are no ongoing calls */ | ||||||
|  | 	if (pool_member->refcount > 0) { | ||||||
|  | 		vty_out(vty, "%% MGCP client (MGW %s) is still serving ongoing calls -- can't reconnect it now!%s", | ||||||
|  | 			mgcp_client_pool_member_name(pool_member), VTY_NEWLINE); | ||||||
|  | 		return CMD_WARNING; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Get rid of a possibly existing old MGCP client instance first */ | ||||||
|  | 	if (pool_member->client) { | ||||||
|  | 		mgcp_client_disconnect(pool_member->client); | ||||||
|  | 		talloc_free(pool_member->client); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Create a new MGCP client instance with the current config */ | ||||||
|  | 	pool_member->client = mgcp_client_init(pool_member, &pool_member->conf); | ||||||
|  | 	if (!pool_member->client) { | ||||||
|  | 		LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s initalization failed\n", | ||||||
|  | 		     mgcp_client_pool_member_name(pool_member)); | ||||||
|  | 		vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s", | ||||||
|  | 			mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE); | ||||||
|  | 		return CMD_WARNING; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Set backpointer so that we can detect later that this MGCP client is managed by this pool. */ | ||||||
|  | 	pool_member->client->pool = global_mgcp_client_pool; | ||||||
|  |  | ||||||
|  | 	/* Connect client */ | ||||||
|  | 	if (mgcp_client_connect(pool_member->client)) { | ||||||
|  | 		LOGP(DLMGCP, LOGL_ERROR, "(manual) MGW %s connect failed at (%s:%u)\n", | ||||||
|  | 		     mgcp_client_pool_member_name(pool_member), pool_member->conf.remote_addr, | ||||||
|  | 		     pool_member->conf.remote_port); | ||||||
|  | 		talloc_free(pool_member->client); | ||||||
|  | 		pool_member->client = NULL; | ||||||
|  | 		vty_out(vty, "%% MGCP client (MGW %s) initalization failed ('%s')%s", | ||||||
|  | 			mgcp_client_pool_member_name(pool_member), argv[0], VTY_NEWLINE); | ||||||
|  | 		return CMD_WARNING; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return CMD_SUCCESS; | 	return CMD_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
| void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *conf) | DEFUN_ATTR(mgw_block, mgw_block_cmd, | ||||||
|  | 	   "mgw <0-255> block", | ||||||
|  | 	   MGW_STR "reference number\n" "block MGCP client so that it won't be used for new calls\n", CMD_ATTR_IMMEDIATE) | ||||||
| { | { | ||||||
| 	global_mgcp_client_ctx = talloc_ctx; | 	int nr = atoi(argv[0]); | ||||||
| 	global_mgcp_client_conf = conf; | 	struct mgcp_client_pool_member *pool_member = NULL; | ||||||
|  |  | ||||||
| 	install_element(node, &cfg_mgcpgw_local_ip_cmd); | 	pool_member = pool_member_by_nr(nr); | ||||||
| 	install_element(node, &cfg_mgcpgw_local_port_cmd); | 	if (!pool_member) { | ||||||
| 	install_element(node, &cfg_mgcpgw_remote_ip_cmd); | 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||||
| 	install_element(node, &cfg_mgcpgw_remote_port_cmd); | 		return CMD_WARNING; | ||||||
| 	install_element(node, &cfg_mgcpgw_endpoint_range_cmd); | 	} | ||||||
| 	install_element(node, &cfg_mgcp_rtp_bts_base_port_cmd); |  | ||||||
|  | 	pool_member->blocked = true; | ||||||
|  | 	return CMD_SUCCESS; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DEFUN_ATTR(mgw_unblock, mgw_unblock_cmd, | ||||||
|  | 	   "mgw <0-255> unblock", | ||||||
|  | 	   MGW_STR "reference number\n" "unblock MGCP client so that it will be available for new calls\n", CMD_ATTR_IMMEDIATE) | ||||||
|  | { | ||||||
|  | 	int nr = atoi(argv[0]); | ||||||
|  | 	struct mgcp_client_pool_member *pool_member = NULL; | ||||||
|  |  | ||||||
|  | 	pool_member = pool_member_by_nr(nr); | ||||||
|  | 	if (!pool_member) { | ||||||
|  | 		vty_out(vty, "%% no such MGCP client configured ('%s')%s", argv[0], VTY_NEWLINE); | ||||||
|  | 		return CMD_WARNING; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pool_member->blocked = false; | ||||||
|  | 	return CMD_SUCCESS; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DEFUN(mgw_show, mgw_snow_cmd, "show mgw-pool", SHOW_STR "Display information about the MGW-Pool\n") | ||||||
|  | { | ||||||
|  | 	vty_out(vty, "%% MGW-Pool:%s", VTY_NEWLINE); | ||||||
|  | 	struct mgcp_client_pool_member *pool_member; | ||||||
|  |  | ||||||
|  | 	if (llist_empty(&global_mgcp_client_pool->pool) && global_mgcp_client_pool->mgcp_client_single) { | ||||||
|  | 		vty_out(vty, "%%  (pool is empty, single MGCP client will be used)%s", VTY_NEWLINE); | ||||||
|  | 		return CMD_SUCCESS; | ||||||
|  | 	} else if (llist_empty(&global_mgcp_client_pool->pool)) { | ||||||
|  | 		vty_out(vty, "%%  (pool is empty)%s", VTY_NEWLINE); | ||||||
|  | 		return CMD_SUCCESS; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	llist_for_each_entry(pool_member, &global_mgcp_client_pool->pool, list) { | ||||||
|  | 		vty_out(vty, "%%  MGW %s%s", mgcp_client_pool_member_name(pool_member), VTY_NEWLINE); | ||||||
|  | 		vty_out(vty, "%%   mgcp-client:   %s%s", pool_member->client ? "connected" : "disconnected", | ||||||
|  | 			VTY_NEWLINE); | ||||||
|  | 		vty_out(vty, "%%   service:       %s%s", pool_member->blocked ? "blocked" : "unblocked", VTY_NEWLINE); | ||||||
|  | 		vty_out(vty, "%%   ongoing calls: %u%s", pool_member->refcount, VTY_NEWLINE); | ||||||
|  | 	} | ||||||
|  | 	return CMD_SUCCESS; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Set up MGCP client VTY (pooled) | ||||||
|  |  *  (called once at startup by the application process). | ||||||
|  |  *  \param[in] parent_node identifier of the parent node on which the mgw node appears. | ||||||
|  |  *  \param[in] mgw_node identifier that should be used with the newly installed MGW node. | ||||||
|  |  *  \param[in] indent indentation string to match the indentation in the VTY config | ||||||
|  |  *  \param[in] pool user provided memory to store the configured MGCP client (MGW) pool. */ | ||||||
|  | void mgcp_client_pool_vty_init(int parent_node, int mgw_node, const char *indent, struct mgcp_client_pool *pool) | ||||||
|  | { | ||||||
|  | 	/* A pool must be allocated before this function can be called */ | ||||||
|  | 	OSMO_ASSERT(pool); | ||||||
|  |  | ||||||
|  | 	/* Never allow this function to be called twice on the same pool */ | ||||||
|  | 	OSMO_ASSERT(!pool->vty_indent); | ||||||
|  | 	OSMO_ASSERT(!pool->vty_node); | ||||||
|  |  | ||||||
|  | 	pool->vty_indent = talloc_strdup(pool, indent); | ||||||
|  | 	OSMO_ASSERT(pool->vty_indent); | ||||||
|  | 	pool->vty_node = talloc_zero(pool, struct cmd_node); | ||||||
|  | 	OSMO_ASSERT(pool->vty_node); | ||||||
|  | 	pool->vty_node->node = mgw_node; | ||||||
|  | 	pool->vty_node->vtysh = 1; | ||||||
|  | 	pool->vty_node->prompt = talloc_strdup(pool->vty_node, "%s(config-mgw)# "); | ||||||
|  |  | ||||||
|  | 	install_lib_element(parent_node, &cfg_mgw_cmd); | ||||||
|  | 	install_lib_element(parent_node, &cfg_no_mgw_cmd); | ||||||
|  |  | ||||||
|  | 	install_node(pool->vty_node, config_write_pool); | ||||||
|  | 	vty_init_common(pool, mgw_node); | ||||||
|  |  | ||||||
|  | 	install_element(mgw_node, &cfg_description_cmd); | ||||||
|  |  | ||||||
|  | 	install_lib_element(ENABLE_NODE, &mgw_reconnect_cmd); | ||||||
|  | 	install_lib_element(ENABLE_NODE, &mgw_block_cmd); | ||||||
|  | 	install_lib_element(ENABLE_NODE, &mgw_unblock_cmd); | ||||||
|  |  | ||||||
|  | 	install_lib_element_ve(&mgw_snow_cmd); | ||||||
|  |  | ||||||
|  | 	global_mgcp_client_pool = pool; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								src/libosmo-mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/libosmo-mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  | AM_CPPFLAGS = \ | ||||||
|  | 	$(all_includes) \ | ||||||
|  | 	-I$(top_srcdir)/include \ | ||||||
|  | 	-I$(top_builddir) \ | ||||||
|  | 	$(NULL) | ||||||
|  |  | ||||||
|  | AM_CFLAGS = \ | ||||||
|  | 	-Wall \ | ||||||
|  | 	$(LIBOSMOCORE_CFLAGS) \ | ||||||
|  | 	$(LIBOSMOGSM_CFLAGS) \ | ||||||
|  | 	$(LIBOSMOVTY_CFLAGS) \ | ||||||
|  | 	$(LIBOSMONETIF_CFLAGS) \ | ||||||
|  | 	$(LIBOSMOABIS_CFLAGS) \ | ||||||
|  | 	$(LIBOSMOTRAU_CFLAGS) \ | ||||||
|  | 	$(COVERAGE_CFLAGS) \ | ||||||
|  | 	$(NULL) | ||||||
|  |  | ||||||
|  | AM_LDFLAGS = \ | ||||||
|  | 	$(LIBOSMOCORE_LIBS) \ | ||||||
|  | 	$(LIBOSMOGSM_LIBS) \ | ||||||
|  | 	$(LIBOSMOVTY_LIBS) \ | ||||||
|  | 	$(LIBOSMONETIF_LIBS) \ | ||||||
|  | 	$(LIBOSMOABIS_LIBS) \ | ||||||
|  | 	$(LIBOSMOTRAU_LIBS) \ | ||||||
|  | 	$(COVERAGE_LDFLAGS) \ | ||||||
|  | 	$(NULL) | ||||||
|  |  | ||||||
|  | noinst_LIBRARIES = \ | ||||||
|  | 	libosmo-mgcp.a \ | ||||||
|  | 	$(NULL) | ||||||
|  |  | ||||||
|  | noinst_HEADERS = \ | ||||||
|  | 	g711common.h \ | ||||||
|  | 	$(NULL) | ||||||
|  |  | ||||||
|  | libosmo_mgcp_a_SOURCES = \ | ||||||
|  | 	mgcp_protocol.c \ | ||||||
|  | 	mgcp_network.c \ | ||||||
|  | 	mgcp_vty.c \ | ||||||
|  | 	mgcp_osmux.c \ | ||||||
|  | 	mgcp_sdp.c \ | ||||||
|  | 	mgcp_codec.c \ | ||||||
|  | 	mgcp_msg.c \ | ||||||
|  | 	mgcp_conn.c \ | ||||||
|  | 	mgcp_stat.c \ | ||||||
|  | 	mgcp_endp.c \ | ||||||
|  | 	mgcp_trunk.c \ | ||||||
|  | 	mgcp_ctrl.c \ | ||||||
|  | 	mgcp_ratectr.c \ | ||||||
|  | 	mgcp_e1.c \ | ||||||
|  | 	$(NULL) | ||||||
| @@ -14,10 +14,6 @@ | |||||||
|  *  but WITHOUT ANY WARRANTY; without even the implied warranty of |  *  but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  *  GNU General Public License for more details. |  *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA |  | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| static inline int val_seg(int val) | static inline int val_seg(int val) | ||||||
							
								
								
									
										459
									
								
								src/libosmo-mgcp/mgcp_codec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										459
									
								
								src/libosmo-mgcp/mgcp_codec.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,459 @@ | |||||||
|  | /* | ||||||
|  |  * (C) 2009-2015 by Holger Hans Peter Freyther <zecke@selfish.org> | ||||||
|  |  * (C) 2009-2014 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 Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | #include <osmocom/mgcp/mgcp.h> | ||||||
|  | #include <osmocom/mgcp/osmux.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_conn.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_protocol.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_endp.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_trunk.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_codec.h> | ||||||
|  | #include <errno.h> | ||||||
|  |  | ||||||
|  | /* Helper function to dump codec information of a specified codec to a printable | ||||||
|  |  * string, used by dump_codec_summary() */ | ||||||
|  | static char *dump_codec(struct mgcp_rtp_codec *codec) | ||||||
|  | { | ||||||
|  | 	static char str[256]; | ||||||
|  | 	char *pt_str; | ||||||
|  |  | ||||||
|  | 	if (codec->payload_type > 76) | ||||||
|  | 		pt_str = "DYNAMIC"; | ||||||
|  | 	else if (codec->payload_type > 72) | ||||||
|  | 		pt_str = "RESERVED <!>"; | ||||||
|  | 	else if (codec->payload_type != PTYPE_UNDEFINED) | ||||||
|  | 		pt_str = codec->subtype_name; | ||||||
|  | 	else | ||||||
|  | 		pt_str = "INVALID <!>"; | ||||||
|  |  | ||||||
|  | 	snprintf(str, sizeof(str), "(pt:%i=%s, audio:%s subt=%s, rate=%u, ch=%i, t=%u/%u)", codec->payload_type, pt_str, | ||||||
|  | 		 codec->audio_name, codec->subtype_name, codec->rate, codec->channels, codec->frame_duration_num, | ||||||
|  | 		 codec->frame_duration_den); | ||||||
|  | 	return str; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Dump a summary of all negotiated codecs to debug log | ||||||
|  |  *  \param[in] conn related rtp-connection. */ | ||||||
|  | void mgcp_codec_summary(struct mgcp_conn_rtp *conn) | ||||||
|  | { | ||||||
|  | 	struct mgcp_rtp_end *rtp; | ||||||
|  | 	unsigned int i; | ||||||
|  | 	struct mgcp_rtp_codec *codec; | ||||||
|  | 	struct mgcp_endpoint *endp; | ||||||
|  |  | ||||||
|  | 	rtp = &conn->end; | ||||||
|  | 	endp = conn->conn->endp; | ||||||
|  |  | ||||||
|  | 	if (rtp->codecs_assigned == 0) { | ||||||
|  | 		LOGPENDP(endp, DLMGCP, LOGL_ERROR, "conn:%s no codecs available\n", | ||||||
|  | 			 mgcp_conn_dump(conn->conn)); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Store parsed codec information */ | ||||||
|  | 	for (i = 0; i < rtp->codecs_assigned; i++) { | ||||||
|  | 		codec = &rtp->codecs[i]; | ||||||
|  |  | ||||||
|  | 		LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s codecs[%u]:%s", | ||||||
|  | 			 mgcp_conn_dump(conn->conn), i, dump_codec(codec)); | ||||||
|  |  | ||||||
|  | 		if (codec == rtp->codec) | ||||||
|  | 			LOGPC(DLMGCP, LOGL_DEBUG, " [selected]"); | ||||||
|  |  | ||||||
|  | 		LOGPC(DLMGCP, LOGL_DEBUG, "\n"); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Initalize or reset codec information with default data. */ | ||||||
|  | static void codec_init(struct mgcp_rtp_codec *codec) | ||||||
|  | { | ||||||
|  | 	*codec = (struct mgcp_rtp_codec){ | ||||||
|  | 		.payload_type = -1, | ||||||
|  | 		.frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM, | ||||||
|  | 		.frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN, | ||||||
|  | 		.rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE, | ||||||
|  | 		.channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS, | ||||||
|  | 		.subtype_name = "", | ||||||
|  | 		.audio_name = "", | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void codec_free(struct mgcp_rtp_codec *codec) | ||||||
|  | { | ||||||
|  | 	*codec = (struct mgcp_rtp_codec){}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Initalize or reset codec information with default data. | ||||||
|  |  *  \param[out] conn related rtp-connection. */ | ||||||
|  | void mgcp_codec_reset_all(struct mgcp_conn_rtp *conn) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	for (i = 0; i < conn->end.codecs_assigned; i++) | ||||||
|  | 		codec_free(&conn->end.codecs[i]); | ||||||
|  | 	conn->end.codecs_assigned = 0; | ||||||
|  | 	conn->end.codec = NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Add codec configuration depending on payload type and/or codec name. This | ||||||
|  |  *  function uses the input parameters to extrapolate the full codec information. | ||||||
|  |  *  \param[out] codec configuration (caller provided memory). | ||||||
|  |  *  \param[out] conn related rtp-connection. | ||||||
|  |  *  \param[in] payload_type codec type id (e.g. 3 for GSM, -1 when undefined). | ||||||
|  |  *  \param[in] audio_name audio codec name, in uppercase (e.g. "GSM/8000/1"). | ||||||
|  |  *  \param[in] param optional codec parameters (set to NULL when unused). | ||||||
|  |  *  \returns 0 on success, -EINVAL on failure. */ | ||||||
|  | int mgcp_codec_add(struct mgcp_conn_rtp *conn, int payload_type, const char *audio_name, const struct mgcp_codec_param *param) | ||||||
|  | { | ||||||
|  | 	int rate; | ||||||
|  | 	int channels; | ||||||
|  | 	struct mgcp_rtp_codec *codec; | ||||||
|  | 	unsigned int pt_offset = conn->end.codecs_assigned; | ||||||
|  |  | ||||||
|  | 	/* The amount of codecs we can store is limited, make sure we do not | ||||||
|  | 	 * overrun this limit. */ | ||||||
|  | 	if (conn->end.codecs_assigned >= MGCP_MAX_CODECS) | ||||||
|  | 		return -EINVAL; | ||||||
|  |  | ||||||
|  | 	/* First unused entry */ | ||||||
|  | 	codec = &conn->end.codecs[conn->end.codecs_assigned]; | ||||||
|  |  | ||||||
|  | 	/* Initalize the codec struct with some default data to begin with */ | ||||||
|  | 	codec_init(codec); | ||||||
|  |  | ||||||
|  | 	if (payload_type != PTYPE_UNDEFINED) { | ||||||
|  | 		/* Make sure we do not get any reserved or undefined type numbers */ | ||||||
|  | 		/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */ | ||||||
|  | 		if ((payload_type == 1 || payload_type == 2 || payload_type == 19) | ||||||
|  | 		    || (payload_type >= 72 && payload_type <= 76) | ||||||
|  | 		    || (payload_type >= 127)) { | ||||||
|  | 			LOGP(DLMGCP, LOGL_ERROR, "Cannot add codec, payload type number %d is reserved\n", | ||||||
|  | 			     payload_type); | ||||||
|  | 			goto error; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		codec->payload_type = payload_type; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* When no audio name is given, we are forced to use the payload | ||||||
|  | 	 * type to generate the audio name. This is only possible for | ||||||
|  | 	 * non dynamic payload types, which are statically defined */ | ||||||
|  | 	if (!audio_name) { | ||||||
|  | 		switch (payload_type) { | ||||||
|  | 		case 0: | ||||||
|  | 			strcpy(codec->audio_name, "PCMU/8000/1"); | ||||||
|  | 			break; | ||||||
|  | 		case 3: | ||||||
|  | 			strcpy(codec->audio_name, "GSM/8000/1"); | ||||||
|  | 			break; | ||||||
|  | 		case 8: | ||||||
|  | 			strcpy(codec->audio_name, "PCMA/8000/1"); | ||||||
|  | 			break; | ||||||
|  | 		case 18: | ||||||
|  | 			strcpy(codec->audio_name, "G729/8000/1"); | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			/* The given payload type is not known to us, or it | ||||||
|  | 			 * it is a dynamic payload type for which we do not | ||||||
|  | 			 * know the audio name. We must give up here */ | ||||||
|  | 			LOGP(DLMGCP, LOGL_ERROR, "No audio codec name given, and payload type %d unknown\n", | ||||||
|  | 			     payload_type); | ||||||
|  | 			goto error; | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		OSMO_STRLCPY_ARRAY(codec->audio_name, audio_name); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Now we extract the codec subtype name, rate and channels. The latter | ||||||
|  | 	 * two are optional. If they are not present we use the safe defaults | ||||||
|  | 	 * above. */ | ||||||
|  | 	if (strlen(codec->audio_name) >= sizeof(codec->subtype_name)) { | ||||||
|  | 		LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(codec->audio_name, -1)); | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  | 	channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS; | ||||||
|  | 	rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; | ||||||
|  | 	if (sscanf(codec->audio_name, "%63[^/]/%d/%d", codec->subtype_name, &rate, &channels) < 1) { | ||||||
|  | 		LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(codec->audio_name, -1)); | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Note: We only accept configurations with one audio channel! */ | ||||||
|  | 	if (channels != 1) { | ||||||
|  | 		LOGP(DLMGCP, LOGL_ERROR, "Cannot handle audio codec with more than one channel: %s\n", | ||||||
|  | 		     osmo_quote_str(codec->audio_name, -1)); | ||||||
|  | 		goto error; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	codec->rate = rate; | ||||||
|  | 	codec->channels = channels; | ||||||
|  | 	codec->payload_type = payload_type; | ||||||
|  |  | ||||||
|  | 	if (!strcmp(codec->subtype_name, "G729")) { | ||||||
|  | 		codec->frame_duration_num = 10; | ||||||
|  | 		codec->frame_duration_den = 1000; | ||||||
|  | 	} else { | ||||||
|  | 		codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; | ||||||
|  | 		codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Derive the payload type if it is unknown */ | ||||||
|  | 	if (codec->payload_type == PTYPE_UNDEFINED) { | ||||||
|  | 		/* TODO: This is semi dead code, see OS#4150 */ | ||||||
|  |  | ||||||
|  | 		/* For the known codecs from the static range we restore | ||||||
|  | 		 * the IANA or 3GPP assigned payload type number */ | ||||||
|  | 		if (codec->rate == 8000 && codec->channels == 1) { | ||||||
|  | 			/* See also: https://www.iana.org/assignments/rtp-parameters/rtp-parameters.xhtml */ | ||||||
|  | 			if (!strcmp(codec->subtype_name, "GSM")) | ||||||
|  | 				codec->payload_type = 3; | ||||||
|  | 			else if (!strcmp(codec->subtype_name, "PCMA")) | ||||||
|  | 				codec->payload_type = 8; | ||||||
|  | 			else if (!strcmp(codec->subtype_name, "PCMU")) | ||||||
|  | 				codec->payload_type = 0; | ||||||
|  | 			else if (!strcmp(codec->subtype_name, "G729")) | ||||||
|  | 				codec->payload_type = 18; | ||||||
|  |  | ||||||
|  | 			/* See also: 3GPP TS 48.103, chapter 5.4.2.2 RTP Payload | ||||||
|  | 			 * Note: These are not fixed payload types as the IANA | ||||||
|  | 			 * defined once, they still remain dymanic payload | ||||||
|  | 			 * types, but with a payload type number preference. */ | ||||||
|  | 			else if (!strcmp(codec->subtype_name, "GSM-EFR")) | ||||||
|  | 				codec->payload_type = 110; | ||||||
|  | 			else if (!strcmp(codec->subtype_name, "GSM-HR-08")) | ||||||
|  | 				codec->payload_type = 111; | ||||||
|  | 			else if (!strcmp(codec->subtype_name, "AMR")) | ||||||
|  | 				codec->payload_type = 112; | ||||||
|  | 			else if (!strcmp(codec->subtype_name, "AMR-WB")) | ||||||
|  | 				codec->payload_type = 113; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		/* If we could not determine a payload type we assume that | ||||||
|  | 		 * we are dealing with a codec from the dynamic range. We | ||||||
|  | 		 * choose a fixed identifier from 96-109. (Note: normally, | ||||||
|  | 		 * the dynamic payload type rante is from 96-127, but from | ||||||
|  | 		 * 110 onwards 3gpp defines prefered codec types, which are | ||||||
|  | 		 * also fixed, see above)  */ | ||||||
|  | 		if (codec->payload_type < 0) { | ||||||
|  | 			/* FIXME: pt_offset is completely unrelated and useless here, any of those numbers may already | ||||||
|  | 			 * have been added to the codecs. Instead, there should be an iterator checking for an actually | ||||||
|  | 			 * unused dynamic payload type number. */ | ||||||
|  | 			codec->payload_type = 96 + pt_offset; | ||||||
|  | 			if (codec->payload_type > 109) { | ||||||
|  | 				LOGP(DLMGCP, LOGL_ERROR, "Ran out of payload type numbers to assign dynamically\n"); | ||||||
|  | 				goto error; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Copy over optional codec parameters */ | ||||||
|  | 	if (param) { | ||||||
|  | 		codec->param = *param; | ||||||
|  | 		codec->param_present = true; | ||||||
|  | 	} else | ||||||
|  | 		codec->param_present = false; | ||||||
|  |  | ||||||
|  | 	conn->end.codecs_assigned++; | ||||||
|  | 	return 0; | ||||||
|  | error: | ||||||
|  | 	/* Make sure we leave a clean codec entry on error. */ | ||||||
|  | 	codec_free(codec); | ||||||
|  | 	return -EINVAL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Check if the given codec is applicable on the specified endpoint | ||||||
|  |  * Helper function for mgcp_codec_decide() */ | ||||||
|  | static bool is_codec_compatible(const struct mgcp_endpoint *endp, const struct mgcp_rtp_codec *codec) | ||||||
|  | { | ||||||
|  | 	/* A codec name must be set, if not, this might mean that the codec | ||||||
|  | 	 * (payload type) that was assigned is unknown to us so we must stop | ||||||
|  | 	 * here. */ | ||||||
|  | 	if (!strlen(codec->subtype_name)) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	/* FIXME: implement meaningful checks to make sure that the given codec | ||||||
|  | 	 * is compatible with the given endpoint */ | ||||||
|  |  | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Decide for one suitable codec | ||||||
|  |  *  \param[in] conn related rtp-connection. | ||||||
|  |  *  \returns 0 on success, -EINVAL on failure. */ | ||||||
|  | int mgcp_codec_decide(struct mgcp_conn_rtp *conn) | ||||||
|  | { | ||||||
|  | 	struct mgcp_rtp_end *rtp; | ||||||
|  | 	unsigned int i; | ||||||
|  | 	struct mgcp_endpoint *endp; | ||||||
|  | 	bool codec_assigned = false; | ||||||
|  |  | ||||||
|  | 	endp = conn->conn->endp; | ||||||
|  | 	rtp = &conn->end; | ||||||
|  |  | ||||||
|  | 	/* This function works on the results the SDP/LCO parser has extracted | ||||||
|  | 	 * from the MGCP message. The goal is to select a suitable codec for | ||||||
|  | 	 * the given connection. When transcoding is available, the first codec | ||||||
|  | 	 * from the codec list is taken without further checking. When | ||||||
|  | 	 * transcoding is not available, then the choice must be made more | ||||||
|  | 	 * carefully. Each codec in the list is checked until one is found that | ||||||
|  | 	 * is rated compatible. The rating is done by the helper function | ||||||
|  | 	 * is_codec_compatible(), which does the actual checking. */ | ||||||
|  | 	for (i = 0; i < rtp->codecs_assigned; i++) { | ||||||
|  | 		/* When no transcoding is available, avoid codecs that would | ||||||
|  | 		 * require transcoding. */ | ||||||
|  | 		if (endp->trunk->no_audio_transcoding && !is_codec_compatible(endp, &rtp->codecs[i])) { | ||||||
|  | 			LOGP(DLMGCP, LOGL_NOTICE, "transcoding not available, skipping codec: %d/%s\n", | ||||||
|  | 			     rtp->codecs[i].payload_type, rtp->codecs[i].subtype_name); | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		rtp->codec = &rtp->codecs[i]; | ||||||
|  | 		codec_assigned = true; | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* FIXME: To the reviewes: This is problematic. I do not get why we | ||||||
|  | 	 * need to reset the packet_duration_ms depending on the codec | ||||||
|  | 	 * selection. I thought it were all 20ms? Is this to address some | ||||||
|  | 	 * cornercase. (This piece of code was in the code path before, | ||||||
|  | 	 * together with the note: "TODO/XXX: Store this per codec and derive | ||||||
|  | 	 * it on use" */ | ||||||
|  | 	if (codec_assigned) { | ||||||
|  | 		if (rtp->maximum_packet_time >= 0 | ||||||
|  | 		    && rtp->maximum_packet_time * rtp->codec->frame_duration_den > | ||||||
|  | 		    rtp->codec->frame_duration_num * 1500) | ||||||
|  | 			rtp->packet_duration_ms = 0; | ||||||
|  |  | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return -EINVAL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Return true if octet-aligned is set in the given codec. Default to octet-aligned=0, i.e. bandwidth-efficient mode. | ||||||
|  |  * See RFC4867 "RTP Payload Format for AMR and AMR-WB" sections "8.1. AMR Media Type Registration" and "8.2. AMR-WB | ||||||
|  |  * Media Type Registration": | ||||||
|  |  * | ||||||
|  |  *    octet-align: Permissible values are 0 and 1.  If 1, octet-aligned | ||||||
|  |  *                 operation SHALL be used.  If 0 or if not present, | ||||||
|  |  *                 bandwidth-efficient operation is employed. | ||||||
|  |  * | ||||||
|  |  * https://tools.ietf.org/html/rfc4867 | ||||||
|  |  */ | ||||||
|  | bool mgcp_codec_amr_is_octet_aligned(const struct mgcp_rtp_codec *codec) | ||||||
|  | { | ||||||
|  | 	if (!codec->param_present) | ||||||
|  | 		return false; | ||||||
|  | 	if (!codec->param.amr_octet_aligned_present) | ||||||
|  | 		return false; | ||||||
|  | 	return codec->param.amr_octet_aligned; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Compare two codecs, all parameters must match up, except for the payload type | ||||||
|  |  * number. */ | ||||||
|  | static bool codecs_same(struct mgcp_rtp_codec *codec_a, struct mgcp_rtp_codec *codec_b) | ||||||
|  | { | ||||||
|  | 	if (codec_a->rate != codec_b->rate) | ||||||
|  | 		return false; | ||||||
|  | 	if (codec_a->channels != codec_b->channels) | ||||||
|  | 		return false; | ||||||
|  | 	if (codec_a->frame_duration_num != codec_b->frame_duration_num) | ||||||
|  | 		return false; | ||||||
|  | 	if (codec_a->frame_duration_den != codec_b->frame_duration_den) | ||||||
|  | 		return false; | ||||||
|  | 	if (strcmp(codec_a->subtype_name, codec_b->subtype_name)) | ||||||
|  | 		return false; | ||||||
|  |  | ||||||
|  | 	/* Note: AMR allows to set the RTP payload format to octet-aligned or bandwith-efficient (octet-aligned=0) | ||||||
|  | 	 * via SDP. This difference concerns payload format only, but not the actual codec. It is not a difference | ||||||
|  | 	 * within the meaning of this function. */ | ||||||
|  |  | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Translate a given payload type number that belongs to the packet of a | ||||||
|  |  *  source connection to the equivalent payload type number that matches the | ||||||
|  |  *  configuration of a destination connection. | ||||||
|  |  *  \param[in] conn_src related source rtp-connection. | ||||||
|  |  *  \param[in] conn_dst related destination rtp-connection. | ||||||
|  |  *  \param[in] payload_type number from the source packet or source connection. | ||||||
|  |  *  \returns translated payload type number on success, -EINVAL on failure. */ | ||||||
|  | int mgcp_codec_pt_translate(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst, int payload_type) | ||||||
|  | { | ||||||
|  | 	struct mgcp_rtp_end *rtp_src; | ||||||
|  | 	struct mgcp_rtp_end *rtp_dst; | ||||||
|  | 	struct mgcp_rtp_codec *codec_src = NULL; | ||||||
|  | 	struct mgcp_rtp_codec *codec_dst = NULL; | ||||||
|  | 	unsigned int i; | ||||||
|  | 	unsigned int codecs_assigned; | ||||||
|  |  | ||||||
|  | 	rtp_src = &conn_src->end; | ||||||
|  | 	rtp_dst = &conn_dst->end; | ||||||
|  |  | ||||||
|  | 	/* Find the codec information that is used on the source side */ | ||||||
|  | 	codecs_assigned = rtp_src->codecs_assigned; | ||||||
|  | 	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS); | ||||||
|  | 	for (i = 0; i < codecs_assigned; i++) { | ||||||
|  | 		if (payload_type == rtp_src->codecs[i].payload_type) { | ||||||
|  | 			codec_src = &rtp_src->codecs[i]; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (!codec_src) | ||||||
|  | 		return -EINVAL; | ||||||
|  |  | ||||||
|  | 	/* Use the codec infrmation from the source and try to find the | ||||||
|  | 	 * equivalent of it on the destination side */ | ||||||
|  | 	codecs_assigned = rtp_dst->codecs_assigned; | ||||||
|  | 	OSMO_ASSERT(codecs_assigned <= MGCP_MAX_CODECS); | ||||||
|  | 	for (i = 0; i < codecs_assigned; i++) { | ||||||
|  | 		if (codecs_same(codec_src, &rtp_dst->codecs[i])) { | ||||||
|  | 			codec_dst = &rtp_dst->codecs[i]; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (!codec_dst) | ||||||
|  | 		return -EINVAL; | ||||||
|  |  | ||||||
|  | 	return codec_dst->payload_type; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Find the payload type number configured for a specific codec by SDP. | ||||||
|  |  * For example, IuUP gets assigned a payload type number, and the endpoint needs to translate that to the number | ||||||
|  |  * assigned to "AMR" on the other conn (by a=rtpmap:N). | ||||||
|  |  * \param conn  The side of an endpoint to get the payload type number for (to translate the payload type number to). | ||||||
|  |  * \param subtype_name  SDP codec name without parameters (e.g. "AMR"). | ||||||
|  |  * \param match_nr  Index for the match found, first being match_nr == 0. Iterate all matches by calling multiple times | ||||||
|  |  *                  with incrementing match_nr. | ||||||
|  |  * \return codec definition for that conn matching the subtype_name, or NULL if no such match_nr is found. | ||||||
|  |  */ | ||||||
|  | const struct mgcp_rtp_codec *mgcp_codec_pt_find_by_subtype_name(struct mgcp_conn_rtp *conn, | ||||||
|  | 								const char *subtype_name, unsigned int match_nr) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	for (i = 0; i < conn->end.codecs_assigned; i++) { | ||||||
|  | 		if (!strcmp(conn->end.codecs[i].subtype_name, subtype_name)) { | ||||||
|  | 			if (match_nr) { | ||||||
|  | 				match_nr--; | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  | 			return &conn->end.codecs[i]; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
							
								
								
									
										409
									
								
								src/libosmo-mgcp/mgcp_conn.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										409
									
								
								src/libosmo-mgcp/mgcp_conn.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,409 @@ | |||||||
|  | /* Message connection list handling */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * Author: Philipp Maier | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <stdatomic.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_conn.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_network.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_protocol.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_common.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_endp.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_trunk.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_sdp.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_codec.h> | ||||||
|  | #include <osmocom/gsm/gsm_utils.h> | ||||||
|  | #include <osmocom/core/rate_ctr.h> | ||||||
|  | #include <osmocom/core/timer.h> | ||||||
|  | #include <ctype.h> | ||||||
|  |  | ||||||
|  | const static struct rate_ctr_group_desc rate_ctr_group_desc = { | ||||||
|  | 	.group_name_prefix = "conn_rtp", | ||||||
|  | 	.group_description = "rtp connection statistics", | ||||||
|  | 	.class_id = 1, | ||||||
|  | 	.num_ctr = ARRAY_SIZE(mgcp_conn_rate_ctr_desc), | ||||||
|  | 	.ctr_desc = mgcp_conn_rate_ctr_desc | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Allocate a new connection identifier. According to RFC3435, they must | ||||||
|  |  * be unique only within the scope of the endpoint. (Caller must provide | ||||||
|  |  * memory for id) */ | ||||||
|  | static int mgcp_alloc_id(struct mgcp_endpoint *endp, char *id) | ||||||
|  | { | ||||||
|  | #define MGCP_CONN_ID_GEN_LEN 8 | ||||||
|  | 	int i; | ||||||
|  | 	int k; | ||||||
|  | 	int rc; | ||||||
|  | 	uint8_t id_bin[MGCP_CONN_ID_GEN_LEN / 2]; | ||||||
|  | 	char *id_hex; | ||||||
|  |  | ||||||
|  | 	/* Generate a connection id that is unique for the current endpoint. | ||||||
|  | 	 * Technically a counter would be sufficient, but in order to | ||||||
|  | 	 * be able to find a specific connection in large logfiles and to | ||||||
|  | 	 * prevent unintentional connections we assign the connection | ||||||
|  | 	 * identifiers randomly from a reasonable large number space */ | ||||||
|  | 	for (i = 0; i < 32; i++) { | ||||||
|  | 		rc = osmo_get_rand_id(id_bin, sizeof(id_bin)); | ||||||
|  | 		if (rc < 0) | ||||||
|  | 			return rc; | ||||||
|  |  | ||||||
|  | 		id_hex = osmo_hexdump_nospc(id_bin, sizeof(id_bin)); | ||||||
|  | 		for (k = 0; k < strlen(id_hex); k++) | ||||||
|  | 			id_hex[k] = toupper(id_hex[k]); | ||||||
|  |  | ||||||
|  | 		/* ensure that the generated conn_id is unique | ||||||
|  | 		 * for this endpoint */ | ||||||
|  | 		if (!mgcp_conn_get_rtp(endp, id_hex)) { | ||||||
|  | 			osmo_strlcpy(id, id_hex, MGCP_CONN_ID_MAXLEN); | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	LOGPENDP(endp, DLMGCP, LOGL_ERROR, "unable to generate a unique connectionIdentifier\n"); | ||||||
|  |  | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Initialize rtp connection struct with default values */ | ||||||
|  | static int mgcp_rtp_conn_init(struct mgcp_conn_rtp *conn_rtp, struct mgcp_conn *conn) | ||||||
|  | { | ||||||
|  | 	struct mgcp_rtp_end *end = &conn_rtp->end; | ||||||
|  | 	/* FIXME: Each new rate counter group requires an unique index. At the | ||||||
|  | 	 * moment we generate this index using this counter, but perhaps there | ||||||
|  | 	 * is a more concious way to assign the indexes. */ | ||||||
|  | 	static atomic_uint rate_ctr_index = 0; | ||||||
|  |  | ||||||
|  | 	conn_rtp->type = MGCP_RTP_DEFAULT; | ||||||
|  | 	conn_rtp->osmux.cid_allocated = false; | ||||||
|  | 	conn_rtp->osmux.cid = 0; | ||||||
|  |  | ||||||
|  | 	/* backpointer to the generic part of the connection */ | ||||||
|  | 	conn->u.rtp.conn = conn; | ||||||
|  |  | ||||||
|  | 	end->rtp.fd = -1; | ||||||
|  | 	end->rtcp.fd = -1; | ||||||
|  | 	end->rtp_port = end->rtcp_port = 0; | ||||||
|  | 	talloc_free(end->fmtp_extra); | ||||||
|  | 	end->fmtp_extra = NULL; | ||||||
|  |  | ||||||
|  | 	/* Set default values */ | ||||||
|  | 	end->frames_per_packet = 0;	/* unknown */ | ||||||
|  | 	end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; | ||||||
|  | 	end->output_enabled = 0; | ||||||
|  | 	end->maximum_packet_time = -1; | ||||||
|  |  | ||||||
|  | 	conn_rtp->rate_ctr_group = rate_ctr_group_alloc(conn, &rate_ctr_group_desc, rate_ctr_index++); | ||||||
|  | 	if (!conn_rtp->rate_ctr_group) | ||||||
|  | 		return -1; | ||||||
|  |  | ||||||
|  | 	conn_rtp->state.in_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, IN_STREAM_ERR_TSTMP_CTR); | ||||||
|  | 	conn_rtp->state.out_stream.err_ts_ctr = rate_ctr_group_get_ctr(conn_rtp->rate_ctr_group, OUT_STREAM_ERR_TSTMP_CTR); | ||||||
|  |  | ||||||
|  | 	/* Make sure codec table is reset */ | ||||||
|  | 	mgcp_codec_reset_all(conn_rtp); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Cleanup rtp connection struct */ | ||||||
|  | static void mgcp_rtp_conn_cleanup(struct mgcp_conn_rtp *conn_rtp) | ||||||
|  | { | ||||||
|  | 	if (mgcp_conn_rtp_is_osmux(conn_rtp)) | ||||||
|  | 		conn_osmux_disable(conn_rtp); | ||||||
|  | 	mgcp_free_rtp_port(&conn_rtp->end); | ||||||
|  | 	rate_ctr_group_free(conn_rtp->rate_ctr_group); | ||||||
|  | 	mgcp_codec_reset_all(conn_rtp); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void mgcp_conn_watchdog_cb(void *data) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn *conn = data; | ||||||
|  | 	LOGPCONN(conn, DLMGCP, LOGL_ERROR, "connection timed out!\n"); | ||||||
|  | 	mgcp_conn_free(conn->endp, conn->id); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void mgcp_conn_watchdog_kick(struct mgcp_conn *conn) | ||||||
|  | { | ||||||
|  | 	int timeout = conn->endp->trunk->cfg->conn_timeout; | ||||||
|  | 	if (!timeout) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "watchdog kicked\n"); | ||||||
|  | 	osmo_timer_schedule(&conn->watchdog, timeout, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! allocate a new connection list entry. | ||||||
|  |  *  \param[in] ctx talloc context | ||||||
|  |  *  \param[in] endp associated endpoint | ||||||
|  |  *  \param[in] id identification number of the connection | ||||||
|  |  *  \param[in] type connection type (e.g. MGCP_CONN_TYPE_RTP) | ||||||
|  |  *  \returns pointer to allocated connection, NULL on error */ | ||||||
|  | struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp, | ||||||
|  | 				  enum mgcp_conn_type type, char *name) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn *conn; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	/* Do not allow more then two connections */ | ||||||
|  | 	if (llist_count(&endp->conns) >= endp->type->max_conns) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	/* Create new connection and add it to the list */ | ||||||
|  | 	conn = talloc_zero(ctx, struct mgcp_conn); | ||||||
|  | 	if (!conn) | ||||||
|  | 		return NULL; | ||||||
|  | 	conn->endp = endp; | ||||||
|  | 	conn->type = type; | ||||||
|  | 	conn->mode = MGCP_CONN_NONE; | ||||||
|  | 	conn->mode_orig = MGCP_CONN_NONE; | ||||||
|  | 	osmo_strlcpy(conn->name, name, sizeof(conn->name)); | ||||||
|  | 	rc = mgcp_alloc_id(endp, conn->id); | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		talloc_free(conn); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch (type) { | ||||||
|  | 	case MGCP_CONN_TYPE_RTP: | ||||||
|  | 		if (mgcp_rtp_conn_init(&conn->u.rtp, conn) < 0) { | ||||||
|  | 			talloc_free(conn); | ||||||
|  | 			return NULL; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		/* NOTE: This should never be called with an | ||||||
|  | 		 * invalid type, its up to the programmer | ||||||
|  | 		 * to ensure propery types */ | ||||||
|  | 		OSMO_ASSERT(false); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Initialize watchdog */ | ||||||
|  | 	osmo_timer_setup(&conn->watchdog, mgcp_conn_watchdog_cb, conn); | ||||||
|  | 	mgcp_conn_watchdog_kick(conn); | ||||||
|  | 	mgcp_endp_add_conn(endp, conn); | ||||||
|  |  | ||||||
|  | 	return conn; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! find a connection by its ID. | ||||||
|  |  *  \param[in] endp associated endpoint | ||||||
|  |  *  \param[in] id identification number of the connection | ||||||
|  |  *  \returns pointer to allocated connection, NULL if not found */ | ||||||
|  | struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, const char *id) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn *conn; | ||||||
|  | 	const char *id_upper; | ||||||
|  | 	const char *conn_id; | ||||||
|  |  | ||||||
|  | 	if (!id || !*id) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	/* Ignore leading zeros in needle */ | ||||||
|  | 	while (*id == '0') | ||||||
|  | 		id++; | ||||||
|  |  | ||||||
|  | 	/* Use uppercase to compare identifiers, to avoid mismatches: RFC3435 2.1.3.2 "Names of | ||||||
|  | 	 * Connections" defines the id as a hex string, so clients may return lower case hex even though | ||||||
|  | 	 * we sent upper case hex in the CRCX response. */ | ||||||
|  | 	id_upper = osmo_str_toupper(id); | ||||||
|  |  | ||||||
|  | 	llist_for_each_entry(conn, &endp->conns, entry) { | ||||||
|  | 		/* Ignore leading zeros in haystack */ | ||||||
|  | 		for (conn_id=conn->id; *conn_id == '0'; conn_id++); | ||||||
|  |  | ||||||
|  | 		if (strcmp(conn_id, id_upper) == 0) | ||||||
|  | 			return conn; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! find an RTP connection by its ID. | ||||||
|  |  *  \param[in] endp associated endpoint | ||||||
|  |  *  \param[in] id identification number of the connection | ||||||
|  |  *  \returns pointer to allocated connection, NULL if not found */ | ||||||
|  | struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, | ||||||
|  | 					const char *id) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn *conn; | ||||||
|  |  | ||||||
|  | 	conn = mgcp_conn_get(endp, id); | ||||||
|  | 	if (!conn) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	if (conn->type == MGCP_CONN_TYPE_RTP) | ||||||
|  | 		return &conn->u.rtp; | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void aggregate_rtp_conn_stats(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn_rtp) | ||||||
|  | { | ||||||
|  | 	struct rate_ctr_group *all_stats = endp->trunk->ratectr.all_rtp_conn_stats; | ||||||
|  | 	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group; | ||||||
|  |  | ||||||
|  | 	if (all_stats == NULL || conn_stats == NULL) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	/* Compared to per-connection RTP statistics, aggregated RTP statistics | ||||||
|  | 	 * contain one additional rate couter item (RTP_NUM_CONNECTIONS). | ||||||
|  | 	 * All other counters in both counter groups correspond to each other. */ | ||||||
|  | 	OSMO_ASSERT(conn_stats->desc->num_ctr + 1 == all_stats->desc->num_ctr); | ||||||
|  |  | ||||||
|  | 	/* all other counters are [now] updated in real-time */ | ||||||
|  | 	rate_ctr_add(rate_ctr_group_get_ctr(all_stats, IN_STREAM_ERR_TSTMP_CTR), | ||||||
|  | 		     rate_ctr_group_get_ctr(conn_stats, IN_STREAM_ERR_TSTMP_CTR)->current); | ||||||
|  | 	rate_ctr_add(rate_ctr_group_get_ctr(all_stats, OUT_STREAM_ERR_TSTMP_CTR), | ||||||
|  | 		     rate_ctr_group_get_ctr(conn_stats, OUT_STREAM_ERR_TSTMP_CTR)->current); | ||||||
|  |  | ||||||
|  | 	rate_ctr_inc(rate_ctr_group_get_ctr(all_stats, RTP_NUM_CONNECTIONS)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! free a connection by its ID. | ||||||
|  |  *  \param[in] endp associated endpoint | ||||||
|  |  *  \param[in] id identification number of the connection */ | ||||||
|  | void mgcp_conn_free(struct mgcp_endpoint *endp, const char *id) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn *conn; | ||||||
|  |  | ||||||
|  | 	conn = mgcp_conn_get(endp, id); | ||||||
|  | 	if (!conn) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	switch (conn->type) { | ||||||
|  | 	case MGCP_CONN_TYPE_RTP: | ||||||
|  | 		aggregate_rtp_conn_stats(endp, &conn->u.rtp); | ||||||
|  | 		mgcp_rtp_conn_cleanup(&conn->u.rtp); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		/* NOTE: This should never be called with an | ||||||
|  | 		 * invalid type, its up to the programmer | ||||||
|  | 		 * to ensure propery types */ | ||||||
|  | 		OSMO_ASSERT(false); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	osmo_timer_del(&conn->watchdog); | ||||||
|  | 	mgcp_endp_remove_conn(endp, conn); | ||||||
|  | 	/* WARN: endp may have be freed after call to mgcp_endp_remove_conn */ | ||||||
|  | 	talloc_free(conn); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! free oldest connection in the list. | ||||||
|  |  *  \param[in] endp associated endpoint */ | ||||||
|  | void mgcp_conn_free_oldest(struct mgcp_endpoint *endp) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn *conn; | ||||||
|  |  | ||||||
|  | 	if (llist_empty(&endp->conns)) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry); | ||||||
|  | 	if (!conn) | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	mgcp_conn_free(endp, conn->id); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! free all connections at once. | ||||||
|  |  *  \param[in] endp associated endpoint */ | ||||||
|  | #if defined(__has_attribute) | ||||||
|  | #if __has_attribute(no_sanitize) | ||||||
|  | __attribute__((no_sanitize("undefined"))) /* ubsan detects a misaligned load */ | ||||||
|  | #endif | ||||||
|  | #endif | ||||||
|  | void mgcp_conn_free_all(struct mgcp_endpoint *endp) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn *conn; | ||||||
|  |  | ||||||
|  | 	/* Drop all items in the list, might be consecutive! */ | ||||||
|  | 	while ((conn = llist_first_entry_or_null(&endp->conns, struct mgcp_conn, entry))) | ||||||
|  | 		mgcp_conn_free(endp, conn->id); | ||||||
|  |  | ||||||
|  | 	return; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! dump basic connection information to human readable string. | ||||||
|  |  *  \param[in] conn to dump | ||||||
|  |  *  \returns human readable string */ | ||||||
|  | char *mgcp_conn_dump(struct mgcp_conn *conn) | ||||||
|  | { | ||||||
|  | 	static char str[sizeof(conn->name)+sizeof(conn->id)+256]; | ||||||
|  | 	char ipbuf[INET6_ADDRSTRLEN]; | ||||||
|  |  | ||||||
|  | 	if (!conn) { | ||||||
|  | 		snprintf(str, sizeof(str), "(null connection)"); | ||||||
|  | 		return str; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch (conn->type) { | ||||||
|  | 	case MGCP_CONN_TYPE_RTP: | ||||||
|  | 		/* Dump RTP connection */ | ||||||
|  | 		snprintf(str, sizeof(str), "(%s/rtp, id:0x%s, ip:%s, " | ||||||
|  | 			 "rtp:%u rtcp:%u)", | ||||||
|  | 			 conn->name, | ||||||
|  | 			 conn->id, | ||||||
|  | 			 osmo_sockaddr_ntop(&conn->u.rtp.end.addr.u.sa, ipbuf), | ||||||
|  | 			 ntohs(conn->u.rtp.end.rtp_port), | ||||||
|  | 			 ntohs(conn->u.rtp.end.rtcp_port)); | ||||||
|  | 		break; | ||||||
|  |  | ||||||
|  | 	default: | ||||||
|  | 		/* Should not happen, we should be able to dump | ||||||
|  | 		 * every possible connection type. */ | ||||||
|  | 		snprintf(str, sizeof(str), "(unknown connection type)"); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return str; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! find destination connection on a specific endpoint. | ||||||
|  |  *  \param[in] conn to search a destination for | ||||||
|  |  *  \returns destination connection, NULL on failure */ | ||||||
|  | struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn) | ||||||
|  | { | ||||||
|  | 	struct mgcp_endpoint *endp; | ||||||
|  | 	struct mgcp_conn *partner_conn; | ||||||
|  | 	endp = conn->endp; | ||||||
|  |  | ||||||
|  | 	/*! NOTE: This simply works by grabbing the first connection that is | ||||||
|  | 	 *  not the supplied connection, which is suitable for endpoints that | ||||||
|  | 	 *  do not serve more than two connections. */ | ||||||
|  |  | ||||||
|  | 	llist_for_each_entry(partner_conn, &endp->conns, entry) { | ||||||
|  | 		if (conn != partner_conn) { | ||||||
|  | 			return partner_conn; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! get oldest connection in the list. | ||||||
|  |  *  \param[in] endp associated endpoint */ | ||||||
|  | struct mgcp_conn *mgcp_conn_get_oldest(struct mgcp_endpoint *endp) | ||||||
|  | { | ||||||
|  | 	if (llist_empty(&endp->conns)) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	return llist_last_entry(&endp->conns, struct mgcp_conn, entry); | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								src/libosmo-mgcp/mgcp_ctrl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/libosmo-mgcp/mgcp_ctrl.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | /* | ||||||
|  |  * (C) 2020 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 Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <osmocom/ctrl/control_if.h> | ||||||
|  | #include <osmocom/mgcp/mgcp.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int mgw_ctrl_node_lookup(void *data, vector vline, int *node_type, | ||||||
|  | 				void **node_data, int *i) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | struct ctrl_handle *mgw_ctrl_interface_setup(struct mgcp_config *cfg, | ||||||
|  | 					     const char *bind_addr, uint16_t port) | ||||||
|  | { | ||||||
|  | 	return ctrl_interface_setup_dynip2(cfg, bind_addr, port, mgw_ctrl_node_lookup, | ||||||
|  | 					   _LAST_CTRL_NODE); | ||||||
|  | } | ||||||
							
								
								
									
										685
									
								
								src/libosmo-mgcp/mgcp_e1.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										685
									
								
								src/libosmo-mgcp/mgcp_e1.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,685 @@ | |||||||
|  | /* E1 traffic handling */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  |  * (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||||
|  |  * All Rights Reserved | ||||||
|  |  * | ||||||
|  |  * Author: Philipp Maier | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU Affero 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 Affero General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Affero General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include <inttypes.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/mgcp/mgcp_protocol.h> | ||||||
|  | #include <osmocom/mgcp/mgcp.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_endp.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_trunk.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_conn.h> | ||||||
|  | #include <osmocom/core/msgb.h> | ||||||
|  | #include <osmocom/abis/e1_input.h> | ||||||
|  | #include <osmocom/abis/abis.h> | ||||||
|  |  | ||||||
|  | #include <osmocom/trau/trau_sync.h> | ||||||
|  | #include <osmocom/trau/trau_frame.h> | ||||||
|  | #include <osmocom/trau/trau_rtp.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_conn.h> | ||||||
|  | #include <osmocom/netif/rtp.h> | ||||||
|  | #include <osmocom/mgcp/debug.h> | ||||||
|  | #include <osmocom/mgcp/mgcp_e1.h> | ||||||
|  | #include <osmocom/codec/codec.h> | ||||||
|  |  | ||||||
|  | #define DEBUG_BITS_MAX 80 | ||||||
|  | #define DEBUG_BYTES_MAX 40 | ||||||
|  | #define DEBUG_E1_TS 0 | ||||||
|  | #define E1_TS_BYTES 160 | ||||||
|  | #define E1_TRAU_BITS 320 | ||||||
|  | #define E1_TRAU_BITS_MSGB 2048 | ||||||
|  |  | ||||||
|  | static struct mgcp_config *cfg; | ||||||
|  |  | ||||||
|  | static const struct e1inp_line_ops dummy_e1_line_ops = { | ||||||
|  | 	.sign_link_up = NULL, | ||||||
|  | 	.sign_link_down = NULL, | ||||||
|  | 	.sign_link = NULL, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* EFR idle frame */ | ||||||
|  | static const ubit_t idle_tf_efr[] = { 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 1, 1, 0, 1, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 1, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				      1, 0, 0, 0, 0, 0, 1, 0, | ||||||
|  | 				      1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* FR idle frame */ | ||||||
|  | static const ubit_t idle_tf_fr[] = { 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 1, 1, 1, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 1, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				     1, 0, 0, 0, 0, 0, 1, 0, | ||||||
|  | 				     1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* Idle speech frame, see also GSM 08.60, chapter 3.4 */ | ||||||
|  | static const ubit_t idle_tf_spch[] = { 0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				       0, 0, 0, 0, 0, 0, 0, 0, | ||||||
|  | 				       1, 0, 1, 1, 1, 0, 0, 0, | ||||||
|  | 				       0, 0, 0, 0, 1, 0, 0, 0, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 0, | ||||||
|  | 				       1, 1, 1, 1, 1, 1, 1, 1, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* If the RTP transmission has dropouts for some reason the I.460 TX-Queue may | ||||||
|  |  * run empty. In order to make sure that the TRAU frame transmission continues | ||||||
|  |  * we generate idle TRAU frames here. */ | ||||||
|  | static void e1_i460_mux_empty_cb(struct osmo_i460_subchan *schan, void *user_data) | ||||||
|  | { | ||||||
|  | 	struct mgcp_endpoint *endp = user_data; | ||||||
|  | 	struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats; | ||||||
|  | 	struct msgb *msg = msgb_alloc_c(endp->trunk, E1_TRAU_BITS_MSGB, "E1-I.460-IDLE-TX-TRAU-frame"); | ||||||
|  | 	uint8_t *ptr; | ||||||
|  | 	const uint8_t *ptr_ft; | ||||||
|  | 	enum osmo_trau_frame_type ft; | ||||||
|  |  | ||||||
|  | 	rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_MUX_EMPTY_CTR)); | ||||||
|  |  | ||||||
|  | 	/* Choose an appropiate idle frame type */ | ||||||
|  | 	ft = endp->e1.trau_rtp_st->type; | ||||||
|  | 	switch (ft) { | ||||||
|  | 	case OSMO_TRAU16_FT_FR: | ||||||
|  | 		ptr_ft = idle_tf_fr; | ||||||
|  | 		break; | ||||||
|  | 	case OSMO_TRAU16_FT_EFR: | ||||||
|  | 		ptr_ft = idle_tf_efr; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		/* FIXME: What about 8k subslots and AMR frames? */ | ||||||
|  | 		ptr_ft = idle_tf_spch; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Put the replacement into a message buffer and enqueue it into the | ||||||
|  | 	 * I.460 multiplexer */ | ||||||
|  | 	ptr = msgb_put(msg, E1_TRAU_BITS); | ||||||
|  | 	memcpy(ptr, ptr_ft, E1_TRAU_BITS); | ||||||
|  | 	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-IDLE-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg), | ||||||
|  | 		 osmo_ubit_dump(msgb_data(msg), msgb_length(msg) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg))); | ||||||
|  | 	osmo_i460_mux_enqueue(endp->e1.schan, msg); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* called by I.460 de-multeiplexer; feed output of I.460 demux into TRAU frame sync */ | ||||||
|  | static void e1_i460_demux_bits_cb(struct osmo_i460_subchan *schan, void *user_data, const ubit_t *bits, | ||||||
|  | 				  unsigned int num_bits) | ||||||
|  | { | ||||||
|  | 	struct mgcp_endpoint *endp = user_data; | ||||||
|  | 	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u bits from subslot: %s...\n", num_bits, | ||||||
|  | 		 osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits)); | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(endp->e1.trau_sync_fi); | ||||||
|  | 	osmo_trau_sync_rx_ubits(endp->e1.trau_sync_fi, bits, num_bits); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* called for each synchronized TRAU frame received; decode frame + convert to RTP | ||||||
|  |  * (the resulting frame will be prepended with an all-zero (12-byte) rtp header) */ | ||||||
|  | static void sync_frame_out_cb(void *user_data, const ubit_t *bits, unsigned int num_bits) | ||||||
|  | { | ||||||
|  | 	unsigned int rtp_hdr_len = sizeof(struct rtp_hdr); | ||||||
|  | 	struct mgcp_endpoint *endp = user_data; | ||||||
|  | 	struct msgb *msg = msgb_alloc_c(endp->trunk, RTP_BUF_SIZE, "RTP-rx-from-E1"); | ||||||
|  | 	struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats; | ||||||
|  | 	struct mgcp_conn *conn_dst; | ||||||
|  | 	struct osmo_trau_frame fr; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	if (!bits || num_bits == 0) | ||||||
|  | 		goto skip; | ||||||
|  |  | ||||||
|  | 	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: receiving %u TRAU frame bits from E1 subslot: %s...\n", | ||||||
|  | 		 num_bits, osmo_ubit_dump(bits, num_bits > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : num_bits)); | ||||||
|  |  | ||||||
|  | 	/* Decode TRAU frame */ | ||||||
|  | 	switch (endp->e1.scd.rate) { | ||||||
|  | 	case OSMO_I460_RATE_8k: | ||||||
|  | 		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 8k trau frame...\n"); | ||||||
|  | 		rc = osmo_trau_frame_decode_8k(&fr, bits, OSMO_TRAU_DIR_UL); | ||||||
|  | 		break; | ||||||
|  | 	case OSMO_I460_RATE_16k: | ||||||
|  | 		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoding 16k trau frame...\n"); | ||||||
|  | 		rc = osmo_trau_frame_decode_16k(&fr, bits, OSMO_TRAU_DIR_UL); | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		/* TRAU frames only exist in 8K or 16K subslots. */ | ||||||
|  | 		OSMO_ASSERT(false); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	if (rc != 0) { | ||||||
|  | 		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to decode trau frame\n"); | ||||||
|  | 		goto skip; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Check if the payload type is supported and what the expected lenth | ||||||
|  | 	 * of the RTP payload will be. */ | ||||||
|  | 	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: decoded trau frame type: %s\n", | ||||||
|  | 		 osmo_trau_frame_type_name(fr.type)); | ||||||
|  |  | ||||||
|  | 	/* Convert decoded trau frame to RTP frame */ | ||||||
|  | 	struct osmo_trau2rtp_state t2rs = { | ||||||
|  | 		.type = fr.type, | ||||||
|  | 	}; | ||||||
|  | 	rc = osmo_trau2rtp(msgb_data(msg) + rtp_hdr_len, msg->data_len - rtp_hdr_len, &fr, &t2rs); | ||||||
|  | 	if (rc <= 0) { | ||||||
|  | 		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: unable to convert trau frame to RTP audio\n"); | ||||||
|  | 		goto skip; | ||||||
|  | 	} | ||||||
|  | 	msgb_put(msg, rtp_hdr_len + rc); | ||||||
|  | 	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-RX: encoded %u bytes of RTP audio: %s\n", rc, | ||||||
|  | 		 osmo_hexdump(msgb_data(msg) + rtp_hdr_len, msgb_length(msg) - rtp_hdr_len)); | ||||||
|  |  | ||||||
|  | 	/* Forward RTP data to IP */ | ||||||
|  | 	conn_dst = llist_first_entry(&endp->conns, struct mgcp_conn, entry); | ||||||
|  | 	if (!conn_dst) { | ||||||
|  | 		LOGPENDP(endp, DE1, LOGL_DEBUG, | ||||||
|  | 			 "E1-I.460-RX: unable to forward RTP audio data from E1: no connection to forward an incoming RTP packet to\n"); | ||||||
|  | 		goto skip; | ||||||
|  | 	} | ||||||
|  | 	OSMO_ASSERT(conn_dst->type == MGCP_CONN_TYPE_RTP); | ||||||
|  |  | ||||||
|  | 	mgcp_send(endp, 1, NULL, msg, &conn_dst->u.rtp, &conn_dst->u.rtp); | ||||||
|  |  | ||||||
|  | 	msgb_free(msg); | ||||||
|  | 	return; | ||||||
|  | skip: | ||||||
|  | 	rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_RX_FAIL_CTR)); | ||||||
|  | 	msgb_free(msg); | ||||||
|  | 	return; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Function to handle outgoing E1 traffic */ | ||||||
|  | static void e1_send(struct e1inp_ts *ts, struct mgcp_trunk *trunk) | ||||||
|  | { | ||||||
|  | 	struct msgb *msg = msgb_alloc_c(trunk, E1_TS_BYTES, "E1-TX-timeslot-bytes"); | ||||||
|  | 	uint8_t *ptr; | ||||||
|  |  | ||||||
|  | 	/* Get E1 frame from I.460 multiplexer */ | ||||||
|  | 	ptr = msgb_put(msg, E1_TS_BYTES); | ||||||
|  | 	osmo_i460_mux_out(&trunk->e1.i460_ts[ts->num - 1], ptr, E1_TS_BYTES); | ||||||
|  |  | ||||||
|  | #if DEBUG_E1_TS == 1 | ||||||
|  | 	LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-TX: (ts:%u) sending %u bytes: %s...\n", ts->num, msgb_length(msg), | ||||||
|  | 		  osmo_hexdump_nospc(msgb_data(msg), | ||||||
|  | 				     msgb_length(msg) > DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg))); | ||||||
|  | #endif | ||||||
|  | 	/* Hand data over to the E1 stack */ | ||||||
|  | 	msgb_enqueue(&ts->raw.tx_queue, msg); | ||||||
|  | 	return; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Callback function to handle incoming E1 traffic */ | ||||||
|  | static void e1_recv_cb(struct e1inp_ts *ts, struct msgb *msg) | ||||||
|  | { | ||||||
|  | 	struct mgcp_trunk *trunk; | ||||||
|  |  | ||||||
|  | 	/* Find associated trunk */ | ||||||
|  | 	trunk = mgcp_trunk_by_line_num(cfg, ts->line->num); | ||||||
|  | 	if (!trunk) { | ||||||
|  | 		LOGP(DE1, LOGL_ERROR, "E1-RX: unable to find a trunk for E1-line %u!\n", ts->line->num); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Check if the incoming data looks sane */ | ||||||
|  | 	if (msgb_length(msg) != E1_TS_BYTES) { | ||||||
|  | 		LOGPTRUNK(trunk, DE1, LOGL_NOTICE, | ||||||
|  | 			  "E1-RX: (ts:%u) expected length is %u, actual length is %u!\n", ts->num, E1_TS_BYTES, | ||||||
|  | 			  msgb_length(msg)); | ||||||
|  | 	} | ||||||
|  | #if DEBUG_E1_TS == 1 | ||||||
|  | 	LOGPTRUNK(trunk, DE1, LOGL_DEBUG, "E1-RX: (ts:%u) receiving %u bytes: %s...\n", ts->num, | ||||||
|  | 		  msgb_length(msg), osmo_hexdump_nospc(msgb_data(msg), | ||||||
|  | 						       msgb_length(msg) > | ||||||
|  | 						       DEBUG_BYTES_MAX ? DEBUG_BYTES_MAX : msgb_length(msg))); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	/* Hand data over to the I.460 demultiplexer. */ | ||||||
|  | 	osmo_i460_demux_in(&trunk->e1.i460_ts[ts->num - 1], msgb_data(msg), msgb_length(msg)); | ||||||
|  |  | ||||||
|  | 	/* Trigger sending of pending E1 traffic */ | ||||||
|  | 	e1_send(ts, trunk); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int e1_init(struct mgcp_trunk *trunk, uint8_t ts_nr) | ||||||
|  | { | ||||||
|  | 	/*! Each timeslot needs only to be configured once. The Timeslot then | ||||||
|  | 	 *  stays open and permanently receives data. It is then up to the | ||||||
|  | 	 *  I.460 demultiplexer to add/remove subchannels as needed. It is | ||||||
|  | 	 *  allowed to call this function multiple times since we check if the | ||||||
|  | 	 *  timeslot is already configured. */ | ||||||
|  |  | ||||||
|  | 	struct e1inp_line *e1_line; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(ts_nr > 0 || ts_nr < NUM_E1_TS); | ||||||
|  | 	cfg = trunk->cfg; | ||||||
|  |  | ||||||
|  | 	if (trunk->e1.ts_in_use[ts_nr - 1]) { | ||||||
|  | 		LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u already set up, skipping...\n", ts_nr); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* Get E1 line */ | ||||||
|  | 	e1_line = e1inp_line_find(trunk->e1.vty_line_nr); | ||||||
|  | 	if (!e1_line) { | ||||||
|  | 		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "no such E1 line %u - check VTY config!\n", | ||||||
|  | 			  trunk->e1.vty_line_nr); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 	e1inp_line_bind_ops(e1_line, &dummy_e1_line_ops); | ||||||
|  |  | ||||||
|  | 	/* Configure E1 timeslot */ | ||||||
|  | 	rc = e1inp_ts_config_raw(&e1_line->ts[ts_nr - 1], e1_line, e1_recv_cb); | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to put E1 timeslot %u in raw mode.\n", ts_nr); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 	rc = e1inp_line_update(e1_line); | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		LOGPTRUNK(trunk, DE1, LOGL_ERROR, "failed to update E1 timeslot %u.\n", ts_nr); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	LOGPTRUNK(trunk, DE1, LOGL_INFO, "E1 timeslot %u set up successfully.\n", ts_nr); | ||||||
|  | 	trunk->e1.ts_in_use[ts_nr - 1] = true; | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Determine a suitable TRAU frame type for a given codec */ | ||||||
|  | static enum osmo_trau_frame_type determine_trau_fr_type(char *sdp_subtype_name, enum osmo_i460_rate i460_rate, | ||||||
|  | 							uint8_t amr_ft, struct mgcp_endpoint *endp) | ||||||
|  | { | ||||||
|  | 	if (strcmp(sdp_subtype_name, "GSM") == 0) | ||||||
|  | 		return OSMO_TRAU16_FT_FR; | ||||||
|  | 	else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0) | ||||||
|  | 		return OSMO_TRAU16_FT_EFR; | ||||||
|  | 	else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) | ||||||
|  | 		return OSMO_TRAU16_FT_HR; | ||||||
|  | 	else if (strcmp(sdp_subtype_name, "AMR") == 0) { | ||||||
|  | 		if (i460_rate == OSMO_I460_RATE_8k) { | ||||||
|  | 			switch (amr_ft) { | ||||||
|  | 			case AMR_4_75: | ||||||
|  | 			case AMR_5_15: | ||||||
|  | 			case AMR_5_90: | ||||||
|  | 				return OSMO_TRAU8_AMR_LOW; | ||||||
|  | 			case AMR_6_70: | ||||||
|  | 				return OSMO_TRAU8_AMR_6k7; | ||||||
|  | 			case AMR_7_40: | ||||||
|  | 				return OSMO_TRAU8_AMR_7k4; | ||||||
|  | 			default: | ||||||
|  | 				LOGPENDP(endp, DE1, LOGL_ERROR, | ||||||
|  | 					 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft); | ||||||
|  | 				return OSMO_TRAU_FT_NONE; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return OSMO_TRAU16_FT_AMR; | ||||||
|  | 	} else { | ||||||
|  | 		LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n", | ||||||
|  | 			 sdp_subtype_name); | ||||||
|  | 		return OSMO_TRAU_FT_NONE; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Determine a suitable TRAU frame type for a given codec */ | ||||||
|  | static enum osmo_tray_sync_pat_id determine_trau_sync_pat(char *sdp_subtype_name, enum osmo_i460_rate i460_rate, | ||||||
|  | 							  uint8_t amr_ft, struct mgcp_endpoint *endp) | ||||||
|  | { | ||||||
|  | 	if (strcmp(sdp_subtype_name, "GSM") == 0) | ||||||
|  | 		return OSMO_TRAU_SYNCP_16_FR_EFR; | ||||||
|  | 	else if (strcmp(sdp_subtype_name, "GSM-EFR") == 0) | ||||||
|  | 		return OSMO_TRAU_SYNCP_16_FR_EFR; | ||||||
|  | 	else if (strcmp(sdp_subtype_name, "GSM-HR-08") == 0) | ||||||
|  | 		return OSMO_TRAU_SYNCP_8_HR; | ||||||
|  | 	else if (strcmp(sdp_subtype_name, "AMR") == 0) { | ||||||
|  | 		if (i460_rate == OSMO_I460_RATE_8k) { | ||||||
|  | 			switch (amr_ft) { | ||||||
|  | 			case AMR_4_75: | ||||||
|  | 			case AMR_5_15: | ||||||
|  | 			case AMR_5_90: | ||||||
|  | 				return OSMO_TRAU_SYNCP_8_AMR_LOW; | ||||||
|  | 			case AMR_6_70: | ||||||
|  | 				return OSMO_TRAU_SYNCP_8_AMR_6K7; | ||||||
|  | 			case AMR_7_40: | ||||||
|  | 				return OSMO_TRAU_SYNCP_8_AMR_7K4; | ||||||
|  | 			default: | ||||||
|  | 				LOGPENDP(endp, DE1, LOGL_ERROR, | ||||||
|  | 					 "E1-TRAU-TX: unsupported or illegal AMR frame type: %u\n", amr_ft); | ||||||
|  | 				return OSMO_TRAU_SYNCP_16_FR_EFR; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return OSMO_TRAU_SYNCP_16_FR_EFR; | ||||||
|  | 	} else { | ||||||
|  | 		LOGPENDP(endp, DE1, LOGL_ERROR, "E1-TRAU-TX: unsupported or illegal codec subtype name: %s\n", | ||||||
|  | 			 sdp_subtype_name); | ||||||
|  | 		return OSMO_TRAU_SYNCP_16_FR_EFR; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Find out if a given TRAU frame type is AMR */ | ||||||
|  | static bool tf_type_is_amr(enum osmo_trau_frame_type ft) | ||||||
|  | { | ||||||
|  |  | ||||||
|  | 	switch (ft) { | ||||||
|  | 	case OSMO_TRAU16_FT_AMR: | ||||||
|  | 	case OSMO_TRAU8_AMR_LOW: | ||||||
|  | 	case OSMO_TRAU8_AMR_6k7: | ||||||
|  | 	case OSMO_TRAU8_AMR_7k4: | ||||||
|  | 		return true; | ||||||
|  | 	default: | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* !Equip E1 endpoint with I.460 mux resources. | ||||||
|  |  *  \param[in] endp endpoint to equip | ||||||
|  |  *  \param[in] ts E1 timeslot number. | ||||||
|  |  *  \param[in] ss E1 subslot number. | ||||||
|  |  *  \param[in] offset E1 bit offset. | ||||||
|  |  *  \returns 0 on success, -EINVAL on error. */ | ||||||
|  | int mgcp_e1_endp_equip(struct mgcp_endpoint *endp, uint8_t ts, uint8_t ss, uint8_t offs) | ||||||
|  | { | ||||||
|  | 	int rc; | ||||||
|  | 	enum osmo_tray_sync_pat_id sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR; | ||||||
|  |  | ||||||
|  | 	OSMO_ASSERT(ts != 0); | ||||||
|  | 	OSMO_ASSERT(ts != 0xFF); | ||||||
|  | 	OSMO_ASSERT(ss != 0xFF); | ||||||
|  | 	OSMO_ASSERT(offs != 0xFF); | ||||||
|  |  | ||||||
|  | 	memset(&endp->e1, 0, sizeof(endp->e1)); | ||||||
|  |  | ||||||
|  | 	endp->e1.last_amr_ft = AMR_4_75; | ||||||
|  |  | ||||||
|  | 	/* Set up E1 line / timeslot */ | ||||||
|  | 	rc = e1_init(endp->trunk, ts); | ||||||
|  | 	if (rc != 0) | ||||||
|  | 		return -EINVAL; | ||||||
|  |  | ||||||
|  | 	/* Set up I.460 mux */ | ||||||
|  | 	switch (e1_rates[ss]) { | ||||||
|  | 	case 64: | ||||||
|  | 		endp->e1.scd.rate = OSMO_I460_RATE_64k; | ||||||
|  | 		endp->e1.scd.demux.num_bits = 160 * 8; | ||||||
|  | 		break; | ||||||
|  | 	case 32: | ||||||
|  | 		endp->e1.scd.rate = OSMO_I460_RATE_32k; | ||||||
|  | 		endp->e1.scd.demux.num_bits = 80 * 8; | ||||||
|  | 		break; | ||||||
|  | 	case 16: | ||||||
|  | 		endp->e1.scd.rate = OSMO_I460_RATE_16k; | ||||||
|  | 		endp->e1.scd.demux.num_bits = 40 * 8; | ||||||
|  | 		sync_pat_id = OSMO_TRAU_SYNCP_16_FR_EFR; | ||||||
|  | 		break; | ||||||
|  | 	case 8: | ||||||
|  | 		endp->e1.scd.rate = OSMO_I460_RATE_8k; | ||||||
|  | 		endp->e1.scd.demux.num_bits = 20 * 8; | ||||||
|  | 		sync_pat_id = OSMO_TRAU_SYNCP_8_HR; | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	endp->e1.scd.bit_offset = offs; | ||||||
|  | 	endp->e1.scd.demux.out_cb_bits = e1_i460_demux_bits_cb; | ||||||
|  | 	endp->e1.scd.demux.out_cb_bytes = NULL; | ||||||
|  | 	endp->e1.scd.demux.user_data = endp; | ||||||
|  | 	endp->e1.scd.mux.in_cb_queue_empty = e1_i460_mux_empty_cb; | ||||||
|  | 	endp->e1.scd.mux.user_data = endp; | ||||||
|  |  | ||||||
|  | 	LOGPENDP(endp, DE1, LOGL_INFO, "adding I.460 subchannel: ts=%u, bit_offset=%u, rate=%uk, num_bits=%lu\n", ts, | ||||||
|  | 		 offs, e1_rates[ss], endp->e1.scd.demux.num_bits); | ||||||
|  | 	endp->e1.schan = osmo_i460_subchan_add(endp, &endp->trunk->e1.i460_ts[ts - 1], &endp->e1.scd); | ||||||
|  | 	if (!endp->e1.schan) { | ||||||
|  | 		LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 subchannel: failed!\n"); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (endp->e1.scd.rate == OSMO_I460_RATE_16k || endp->e1.scd.rate == OSMO_I460_RATE_8k) { | ||||||
|  | 		/* TRAU frames are only specified for 16k and 8k subslots. For all other subslot | ||||||
|  | 		 * types the concept of TRAU frames does not apply. However, at the moment this | ||||||
|  | 		 * is the only format we currently support in osmo-mgw */ | ||||||
|  | 		endp->e1.trau_sync_fi = osmo_trau_sync_alloc(endp, "trau-sync", sync_frame_out_cb, sync_pat_id, endp); | ||||||
|  | 		if (!endp->e1.trau_sync_fi) { | ||||||
|  | 			LOGPENDP(endp, DE1, LOGL_ERROR, "adding I.460 TRAU frame sync: failed!\n"); | ||||||
|  | 			return -EINVAL; | ||||||
|  | 		} | ||||||
|  | 		endp->e1.trau_rtp_st = talloc_zero(endp->e1.trau_sync_fi, struct osmo_trau2rtp_state); | ||||||
|  | 		endp->e1.trau_rtp_st->type = OSMO_TRAU_FT_NONE; | ||||||
|  | 	} else { | ||||||
|  | 		LOGPENDP(endp, DE1, LOGL_ERROR, | ||||||
|  | 			 "osmo-mgw currently only supports 16K and 8K subslots (TRAU frames)!\n"); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Update E1 related parameters (codec and sync pattern). | ||||||
|  |  *  \param[in] endp endpoint to update. */ | ||||||
|  | void mgcp_e1_endp_update(struct mgcp_endpoint *endp) | ||||||
|  | { | ||||||
|  | 	struct mgcp_conn *conn; | ||||||
|  | 	struct mgcp_rtp_codec *codec; | ||||||
|  | 	enum osmo_tray_sync_pat_id sync_pat_id; | ||||||
|  |  | ||||||
|  | 	/* In order to determine the codec, find the oldest connection on | ||||||
|  | 	 * the endpoint and use its codec information. Normally on an E1 | ||||||
|  | 	 * endpoint no more than one connection should exist. */ | ||||||
|  | 	conn = mgcp_conn_get_oldest(endp); | ||||||
|  | 	OSMO_ASSERT(conn); | ||||||
|  | 	codec = conn->u.rtp.end.codec; | ||||||
|  | 	OSMO_ASSERT(codec); | ||||||
|  |  | ||||||
|  | 	/* Update codec information */ | ||||||
|  | 	endp->e1.trau_rtp_st->type = | ||||||
|  | 	    determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp); | ||||||
|  | 	endp->e1.last_codec = codec; | ||||||
|  |  | ||||||
|  | 	/* Update sync pattern */ | ||||||
|  | 	sync_pat_id = determine_trau_sync_pat(codec->subtype_name, endp->e1.scd.rate, endp->e1.last_amr_ft, endp); | ||||||
|  | 	osmo_trau_sync_set_pat(endp->e1.trau_sync_fi, sync_pat_id); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Remove E1 resources from endpoint | ||||||
|  |  *  \param[in] endp endpoint to release. */ | ||||||
|  | void mgcp_e1_endp_release(struct mgcp_endpoint *endp) | ||||||
|  | { | ||||||
|  | 	LOGPENDP(endp, DE1, LOGL_DEBUG, "removing I.460 subchannel and sync...\n"); | ||||||
|  |  | ||||||
|  | 	if (endp->e1.schan) | ||||||
|  | 		osmo_i460_subchan_del(endp->e1.schan); | ||||||
|  | 	if (endp->e1.trau_rtp_st) | ||||||
|  | 		talloc_free(endp->e1.trau_rtp_st); | ||||||
|  | 	if (endp->e1.trau_sync_fi) | ||||||
|  | 		osmo_fsm_inst_term(endp->e1.trau_sync_fi, OSMO_FSM_TERM_REGULAR, NULL); | ||||||
|  |  | ||||||
|  | 	memset(&endp->e1, 0, sizeof(endp->e1)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*! Accept RTP message buffer with RTP data and enqueue voice data for E1 transmit. | ||||||
|  |  *  \param[in] endp related endpoint (does not take ownership). | ||||||
|  |  *  \param[in] codec configuration. | ||||||
|  |  *  \param[in] msg RTP message buffer (including RTP header). | ||||||
|  |  *  \returns 0 on success, -1 on ERROR. */ | ||||||
|  | int mgcp_e1_send_rtp(struct mgcp_endpoint *endp, struct mgcp_rtp_codec *codec, struct msgb *msg) | ||||||
|  | { | ||||||
|  | 	struct msgb *msg_tf = msgb_alloc_c(endp->trunk, E1_TRAU_BITS_MSGB, "E1-I.460-TX-TRAU-frame"); | ||||||
|  | 	struct rate_ctr_group *rate_ctrs = endp->trunk->ratectr.e1_stats; | ||||||
|  | 	unsigned int rtp_hdr_len = sizeof(struct rtp_hdr); | ||||||
|  | 	struct osmo_trau_frame tf; | ||||||
|  | 	uint8_t amr_ft; | ||||||
|  | 	int rc; | ||||||
|  |  | ||||||
|  | 	/* Extract AMR frame type from AMR head (if AMR is used) */ | ||||||
|  | 	if (tf_type_is_amr(endp->e1.trau_rtp_st->type)) | ||||||
|  | 		amr_ft = (msgb_data(msg)[rtp_hdr_len + 1] >> 3) & 0xf; | ||||||
|  | 	else | ||||||
|  | 		amr_ft = 0xff; | ||||||
|  |  | ||||||
|  | 	/* Adapt TRAU frame type on codec changes */ | ||||||
|  | 	OSMO_ASSERT(endp->e1.last_codec); | ||||||
|  | 	if (codec != endp->e1.last_codec || (amr_ft != 0xff && amr_ft != endp->e1.last_amr_ft)) { | ||||||
|  | 		endp->e1.trau_rtp_st->type = | ||||||
|  | 		    determine_trau_fr_type(codec->subtype_name, endp->e1.scd.rate, amr_ft, endp); | ||||||
|  | 		endp->e1.last_codec = codec; | ||||||
|  | 		endp->e1.last_amr_ft = amr_ft; | ||||||
|  | 	} | ||||||
|  | 	if (endp->e1.trau_rtp_st->type == OSMO_TRAU_FT_NONE) | ||||||
|  | 		goto skip; | ||||||
|  | 	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: using trau frame type for encoding: %s\n", | ||||||
|  | 		 osmo_trau_frame_type_name(endp->e1.trau_rtp_st->type)); | ||||||
|  |  | ||||||
|  | 	/* Convert from RTP to TRAU format */ | ||||||
|  | 	msg->l2h = msgb_data(msg) + rtp_hdr_len; | ||||||
|  | 	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: decoding %u bytes of RTP audio to TRAU format: %s\n", | ||||||
|  | 		 msgb_length(msg), osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); | ||||||
|  | 	memset(&tf, 0, sizeof(tf)); | ||||||
|  | 	tf.dir = OSMO_TRAU_DIR_DL; | ||||||
|  | 	rc = osmo_rtp2trau(&tf, msgb_l2(msg), msgb_l2len(msg), endp->e1.trau_rtp_st); | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		LOGPENDP(endp, DE1, LOGL_DEBUG, | ||||||
|  | 			 "E1-I.460-TX: failed to decode from RTP payload format to TRAU format\n"); | ||||||
|  | 		goto skip; | ||||||
|  | 	} | ||||||
|  | 	rc = osmo_trau_frame_encode(msgb_data(msg_tf), msg_tf->data_len, &tf); | ||||||
|  | 	if (rc < 0) { | ||||||
|  | 		LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: failed to encode TRAU frame\n"); | ||||||
|  | 		goto skip; | ||||||
|  | 	} | ||||||
|  | 	msgb_put(msg_tf, rc); | ||||||
|  | 	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: enquing %u trau frame bits: %s...\n", msgb_length(msg_tf), | ||||||
|  | 		 osmo_ubit_dump(msgb_data(msg_tf), | ||||||
|  | 				msgb_length(msg_tf) > DEBUG_BITS_MAX ? DEBUG_BITS_MAX : msgb_length(msg_tf))); | ||||||
|  |  | ||||||
|  | 	/* Enqueue data to I.460 multiplexer */ | ||||||
|  | 	OSMO_ASSERT(endp->e1.schan); | ||||||
|  | 	OSMO_ASSERT(endp->e1.trau_sync_fi); | ||||||
|  |  | ||||||
|  | 	osmo_i460_mux_enqueue(endp->e1.schan, msg_tf); | ||||||
|  | 	LOGPENDP(endp, DE1, LOGL_DEBUG, "E1-I.460-TX: %u bits of audio enqued for E1 tx\n", msgb_length(msg_tf)); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | skip: | ||||||
|  | 	rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, E1_I460_TRAU_TX_FAIL_CTR)); | ||||||
|  | 	msgb_free(msg_tf); | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user