mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
				synced 2025-11-03 21:43:32 +00:00 
			
		
		
		
	Compare commits
	
		
			381 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					306dc09d37 | ||
| 
						 | 
					eb282efa67 | ||
| 
						 | 
					02d5c8798b | ||
| 
						 | 
					efe3f98ec7 | ||
| 
						 | 
					3ebf7e2f05 | ||
| 
						 | 
					33826246c5 | ||
| 
						 | 
					9ef0c11abd | ||
| 
						 | 
					109ff5244e | ||
| 
						 | 
					4cf5e71a4b | ||
| 
						 | 
					8c6f3141e2 | ||
| 
						 | 
					12220eea9c | ||
| 
						 | 
					536dff1bb8 | ||
| 
						 | 
					ee784f9844 | ||
| 
						 | 
					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 | 
							
								
								
									
										50
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										50
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -2,26 +2,20 @@ debian/*.log
 | 
			
		||||
*.o
 | 
			
		||||
*.lo
 | 
			
		||||
*.a
 | 
			
		||||
*.la
 | 
			
		||||
.deps
 | 
			
		||||
Makefile
 | 
			
		||||
Makefile.in
 | 
			
		||||
bscconfig.h
 | 
			
		||||
bscconfig.h.in
 | 
			
		||||
openbsc.pc
 | 
			
		||||
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
 | 
			
		||||
src/osmo-mgw/osmo-mgw
 | 
			
		||||
*.*~
 | 
			
		||||
*.sw?
 | 
			
		||||
.libs
 | 
			
		||||
*.pyc
 | 
			
		||||
*.gcda
 | 
			
		||||
*.gcno
 | 
			
		||||
*.pc
 | 
			
		||||
 | 
			
		||||
#configure
 | 
			
		||||
aclocal.m4
 | 
			
		||||
@@ -38,38 +32,16 @@ missing
 | 
			
		||||
stamp-h1
 | 
			
		||||
libtool
 | 
			
		||||
ltmain.sh
 | 
			
		||||
m4/*.m4
 | 
			
		||||
 | 
			
		||||
# git-version-gen magic
 | 
			
		||||
.tarball-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/testsuite.dir
 | 
			
		||||
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/atlocal
 | 
			
		||||
@@ -77,7 +49,15 @@ tests/package.m4
 | 
			
		||||
tests/testsuite
 | 
			
		||||
tests/testsuite.log
 | 
			
		||||
 | 
			
		||||
gsn_restart
 | 
			
		||||
src/openbsc.cfg*
 | 
			
		||||
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
 | 
			
		||||
 
 | 
			
		||||
@@ -18,13 +18,15 @@ SUBDIRS = \
 | 
			
		||||
 | 
			
		||||
pkgconfigdir = $(libdir)/pkgconfig
 | 
			
		||||
pkgconfig_DATA = \
 | 
			
		||||
	libosmo-legacy-mgcp.pc \
 | 
			
		||||
	libosmo-mgcp-client.pc \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
BUILT_SOURCES = $(top_srcdir)/.version
 | 
			
		||||
EXTRA_DIST = git-version-gen osmoappdesc.py .version
 | 
			
		||||
 | 
			
		||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
 | 
			
		||||
	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir)
 | 
			
		||||
 | 
			
		||||
@RELMAKE@
 | 
			
		||||
 | 
			
		||||
$(top_srcdir)/.version:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
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
 | 
			
		||||
https://osmocom.org/projects/osmo-mgw
 | 
			
		||||
https://osmocom.org/projects/osmo-mgw/wiki
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								TODO-RELEASE
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								TODO-RELEASE
									
									
									
									
									
								
							@@ -1,9 +1,26 @@
 | 
			
		||||
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
 | 
			
		||||
# according to https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
 | 
			
		||||
# In short:
 | 
			
		||||
# LIBVERSION=c:r:a
 | 
			
		||||
# 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.
 | 
			
		||||
# If any interfaces have been added since the last public release: c:r:a + 1.
 | 
			
		||||
# If any interfaces have been removed or changed since the last public release: c:r:0.
 | 
			
		||||
#library	what		description / commit summary line
 | 
			
		||||
# When cleaning up this file upon a release:
 | 
			
		||||
#
 | 
			
		||||
# - Note that the release version number is entirely unrelated to the API
 | 
			
		||||
#   versions. A release version 5.2.3 may happily have an API version of 42:7:5.
 | 
			
		||||
#
 | 
			
		||||
# - Bump API version in src/lib*/Makefile.am files according to chapter
 | 
			
		||||
#   "Library interface versions" of the libtool documentation.
 | 
			
		||||
#   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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										135
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								configure.ac
									
									
									
									
									
								
							@@ -39,43 +39,47 @@ AC_SEARCH_LIBS([dlopen], [dl dld], [LIBRARY_DL="$LIBS";LIBS=""])
 | 
			
		||||
AC_SUBST(LIBRARY_DL)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.0.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMONETIF, libosmo-netif >= 0.6.0)
 | 
			
		||||
 | 
			
		||||
# Enable/disable transcoding within osmo-bsc_mgcp?
 | 
			
		||||
AC_ARG_ENABLE([mgcp-transcoding], [AS_HELP_STRING([--enable-mgcp-transcoding], [Build the MGCP gateway with internal transcoding enabled.])],
 | 
			
		||||
    [osmo_ac_mgcp_transcoding="$enableval"],[osmo_ac_mgcp_transcoding="no"])
 | 
			
		||||
AC_ARG_WITH([g729], [AS_HELP_STRING([--with-g729], [Enable G.729 encoding/decoding.])], [osmo_ac_with_g729="$withval"],[osmo_ac_with_g729="no"])
 | 
			
		||||
 | 
			
		||||
if test "$osmo_ac_mgcp_transcoding" = "yes" ; then
 | 
			
		||||
    AC_SEARCH_LIBS([gsm_create], [gsm], [LIBRARY_GSM="$LIBS";LIBS=""], [AC_MSG_ERROR([--enable-mgcp-transcoding: cannot find usable libgsm])])
 | 
			
		||||
    AC_SUBST(LIBRARY_GSM)
 | 
			
		||||
    if test "$osmo_ac_with_g729" = "yes" ; then
 | 
			
		||||
	PKG_CHECK_MODULES(LIBBCG729, libbcg729 >= 0.1, [AC_DEFINE([HAVE_BCG729], [1], [Use bgc729 decoder/encoder])])
 | 
			
		||||
    fi
 | 
			
		||||
    AC_DEFINE(BUILD_MGCP_TRANSCODING, 1, [Define if we want to build the MGCP gateway with transcoding support])
 | 
			
		||||
AC_ARG_ENABLE(sanitize,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
		[--enable-sanitize],
 | 
			
		||||
		[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
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(werror,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
		[--enable-werror],
 | 
			
		||||
		[Turn all compiler warnings into errors, with exceptions:
 | 
			
		||||
		 a) deprecation (allow upstream to mark deprecation without breaking builds);
 | 
			
		||||
		 b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds)
 | 
			
		||||
		]
 | 
			
		||||
	)],
 | 
			
		||||
	[werror=$enableval], [werror="no"])
 | 
			
		||||
if test x"$werror" = x"yes"
 | 
			
		||||
then
 | 
			
		||||
	WERROR_FLAGS="-Werror"
 | 
			
		||||
	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations"
 | 
			
		||||
	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning"
 | 
			
		||||
	CFLAGS="$CFLAGS $WERROR_FLAGS"
 | 
			
		||||
	CPPFLAGS="$CPPFLAGS $WERROR_FLAGS"
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
# The following test is taken from WebKit's webkit.m4
 | 
			
		||||
saved_CFLAGS="$CFLAGS"
 | 
			
		||||
CFLAGS="$CFLAGS -fvisibility=hidden "
 | 
			
		||||
AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
 | 
			
		||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])],
 | 
			
		||||
      [ AC_MSG_RESULT([yes])
 | 
			
		||||
        SYMBOL_VISIBILITY="-fvisibility=hidden"],
 | 
			
		||||
        AC_MSG_RESULT([no]))
 | 
			
		||||
CFLAGS="$saved_CFLAGS"
 | 
			
		||||
AC_SUBST(SYMBOL_VISIBILITY)
 | 
			
		||||
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=implicit], [CFLAGS="$CFLAGS -Werror=implicit"])
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=maybe-uninitialized], [CFLAGS="$CFLAGS -Werror=maybe-uninitialized"])
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=memset-transposed-args], [CFLAGS="$CFLAGS -Werror=memset-transposed-args"])
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Werror=null-dereference], [CFLAGS="$CFLAGS -Werror=null-dereference"])
 | 
			
		||||
AX_CHECK_COMPILE_FLAG([-Wnull-dereference], [CFLAGS="$CFLAGS -Wnull-dereference"])
 | 
			
		||||
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"])
 | 
			
		||||
 | 
			
		||||
@@ -113,25 +117,88 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
 | 
			
		||||
AC_MSG_RESULT([$enable_ext_tests])
 | 
			
		||||
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
 | 
			
		||||
AM_CONFIG_HEADER(bscconfig.h)
 | 
			
		||||
 | 
			
		||||
AC_OUTPUT(
 | 
			
		||||
    libosmo-legacy-mgcp.pc
 | 
			
		||||
    libosmo-mgcp-client.pc
 | 
			
		||||
    include/Makefile
 | 
			
		||||
    include/osmocom/Makefile
 | 
			
		||||
    include/osmocom/legacy_mgcp/Makefile
 | 
			
		||||
    include/osmocom/mgcp_client/Makefile
 | 
			
		||||
    include/osmocom/mgcp/Makefile
 | 
			
		||||
    src/Makefile
 | 
			
		||||
    src/libosmo-legacy-mgcp/Makefile
 | 
			
		||||
    src/libosmo-mgcp-client/Makefile
 | 
			
		||||
    src/osmo-bsc_mgcp/Makefile
 | 
			
		||||
    src/libosmo-mgcp/Makefile
 | 
			
		||||
    src/osmo-mgw/Makefile
 | 
			
		||||
    tests/Makefile
 | 
			
		||||
    tests/atlocal
 | 
			
		||||
    tests/legacy_mgcp/Makefile
 | 
			
		||||
    tests/mgcp_client/Makefile
 | 
			
		||||
    tests/mgcp/Makefile
 | 
			
		||||
    tests/iuup/Makefile
 | 
			
		||||
    doc/Makefile
 | 
			
		||||
    doc/examples/Makefile
 | 
			
		||||
    doc/manuals/Makefile
 | 
			
		||||
    contrib/Makefile
 | 
			
		||||
    contrib/systemd/Makefile
 | 
			
		||||
    Makefile)
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +1,3 @@
 | 
			
		||||
SUBDIRS = systemd
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = ipa.py
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,10 @@
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
# 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
 | 
			
		||||
	echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
 | 
			
		||||
@@ -14,19 +19,27 @@ deps="$base/deps"
 | 
			
		||||
inst="$deps/install"
 | 
			
		||||
export deps inst
 | 
			
		||||
 | 
			
		||||
mkdir "$deps" || true
 | 
			
		||||
rm -rf "$inst"
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
 | 
			
		||||
mkdir "$deps" || true
 | 
			
		||||
osmo-build-dep.sh libosmocore "" ac_cv_path_DOXYGEN=false
 | 
			
		||||
 | 
			
		||||
verify_value_string_arrays_are_terminated.py $(find . -name "*.[hc]")
 | 
			
		||||
 | 
			
		||||
export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
 | 
			
		||||
export LD_LIBRARY_PATH="$inst/lib"
 | 
			
		||||
export PATH="$inst/bin:$PATH"
 | 
			
		||||
 | 
			
		||||
osmo-build-dep.sh libosmo-abis
 | 
			
		||||
osmo-build-dep.sh libosmo-netif
 | 
			
		||||
 | 
			
		||||
# Additional configure options and depends
 | 
			
		||||
CONFIG=""
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ]; then
 | 
			
		||||
	osmo-build-dep.sh osmo-gsm-manuals
 | 
			
		||||
	CONFIG="--enable-manuals"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
set +x
 | 
			
		||||
echo
 | 
			
		||||
echo
 | 
			
		||||
@@ -37,11 +50,18 @@ set -x
 | 
			
		||||
 | 
			
		||||
cd "$base"
 | 
			
		||||
autoreconf --install --force
 | 
			
		||||
./configure $MGCP --enable-vty-tests --enable-external-tests
 | 
			
		||||
./configure --enable-vty-tests --enable-external-tests --enable-werror $CONFIG
 | 
			
		||||
$MAKE $PARALLEL_MAKE
 | 
			
		||||
LD_LIBRARY_PATH="$inst/lib" $MAKE check \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
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 \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
 | 
			
		||||
	make -C "$base/doc/manuals" publish
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
$MAKE maintainer-clean
 | 
			
		||||
osmo-clean-workspace.sh
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
							
								
								
									
										422
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										422
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,425 @@
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
  * First release after major rename.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -7,7 +7,8 @@ Build-Depends: debhelper (>=9),
 | 
			
		||||
               pkg-config,
 | 
			
		||||
               autotools-dev,
 | 
			
		||||
               libosmocore-dev,
 | 
			
		||||
               libosmo-netif-dev
 | 
			
		||||
               libosmo-netif-dev,
 | 
			
		||||
               osmo-gsm-manuals-dev
 | 
			
		||||
Standards-Version: 3.9.8
 | 
			
		||||
Vcs-Git: git://git.osmocom.org/osmo-mgw.git
 | 
			
		||||
Vcs-Browser: https://git.osmocom.org/osmo-mgw/
 | 
			
		||||
@@ -16,25 +17,10 @@ Homepage: https://osmocom.org/projects/osmo-mgw
 | 
			
		||||
Package: osmo-mgw
 | 
			
		||||
Architecture: any
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
Package: libosmo-legacy-mgcp0
 | 
			
		||||
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
 | 
			
		||||
Package: libosmo-mgcp-client6
 | 
			
		||||
Section: libs
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
@@ -46,5 +32,14 @@ Package: libosmo-mgcp-client-dev
 | 
			
		||||
Section: libdevel
 | 
			
		||||
Architecture: any
 | 
			
		||||
Multi-Arch: same
 | 
			
		||||
Depends: libosmo-mgcp-client1 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
Depends: libosmo-mgcp-client6 (= ${binary:Version}), ${misc:Depends}
 | 
			
		||||
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
 | 
			
		||||
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
Files:     src/libosmo-legacy-mgcp/g711common.h
 | 
			
		||||
Copyright: 2000 Abramo Bagnara <abramo@alsa-project.org>
 | 
			
		||||
Files:     src/libosmo-mgcp-client/* include/osmocom/mgcp_client/*
 | 
			
		||||
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+
 | 
			
		||||
 Wrapper for linphone Codec class by Simon Morlat <simon.morlat@linphone.org>
 | 
			
		||||
 .
 | 
			
		||||
 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
 | 
			
		||||
@@ -37,13 +38,7 @@ License:   GPL-2.0+
 | 
			
		||||
 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
 | 
			
		||||
 .
 | 
			
		||||
 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'.
 | 
			
		||||
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
Files:     tests/vty_test_runner.py
 | 
			
		||||
Copyright: 2013 Holger Hans Peter Freyther
 | 
			
		||||
@@ -68,15 +63,3 @@ License:   GPL-3.0+
 | 
			
		||||
Files:     osmoappdesc.py
 | 
			
		||||
Copyright: 2013 Katerina Barone-Adesi <kat.obsc@gmail.com>
 | 
			
		||||
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-client6.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/libosmo-mgcp-client6.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
 | 
			
		||||
							
								
								
									
										5
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								debian/osmo-mgw.install
									
									
									
									
										vendored
									
									
								
							@@ -1 +1,4 @@
 | 
			
		||||
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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							@@ -14,7 +14,7 @@ CFLAGS += -g
 | 
			
		||||
 | 
			
		||||
# main packaging script based on dh7 syntax
 | 
			
		||||
%:
 | 
			
		||||
	dh $@ --with autoreconf 
 | 
			
		||||
	dh $@ --with autoreconf
 | 
			
		||||
 | 
			
		||||
# debmake generated override targets
 | 
			
		||||
# Set options for ./configure
 | 
			
		||||
@@ -26,8 +26,14 @@ CFLAGS += -g
 | 
			
		||||
#override_dh_install:
 | 
			
		||||
#	dh_install --list-missing -X.la -X.pyc -X.pyo
 | 
			
		||||
 | 
			
		||||
override_dh_autoreconf:
 | 
			
		||||
	echo $(VERSION) > .tarball-version
 | 
			
		||||
	dh_autoreconf
 | 
			
		||||
override_dh_auto_test:
 | 
			
		||||
	dh_auto_test || (find . -name testsuite.log -exec cat {} \; ; false)
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	examples \
 | 
			
		||||
	manuals \
 | 
			
		||||
	$(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),,'
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										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 10.9.1.122
 | 
			
		||||
  rtp ip-probing
 | 
			
		||||
  rtp ip-tos 184
 | 
			
		||||
  bind port 2427
 | 
			
		||||
  sdp audio payload number 98
 | 
			
		||||
  sdp audio payload name GSM
 | 
			
		||||
  number endpoints 31
 | 
			
		||||
  loop 0
 | 
			
		||||
  force-realloc 1
 | 
			
		||||
  rtcp-omit
 | 
			
		||||
  rtp-patch ssrc
 | 
			
		||||
  rtp-patch timestamp
 | 
			
		||||
							
								
								
									
										18
									
								
								doc/manuals/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								doc/manuals/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
  OSMO_REPOSITORY = osmo-mgw
 | 
			
		||||
  include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
 | 
			
		||||
endif
 | 
			
		||||
							
								
								
									
										57
									
								
								doc/manuals/chapters/configuration.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								doc/manuals/chapters/configuration.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
			
		||||
== 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 (trunk 0) 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 which are not yet implemented. Don't use trunks other than the
 | 
			
		||||
"virtual" trunk 0.
 | 
			
		||||
 | 
			
		||||
.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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
							
								
								
									
										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>>
 | 
			
		||||
							
								
								
									
										100
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								doc/manuals/chapters/overview.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
[[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 -> OsmoMSC [label="3GPP AoIP"];
 | 
			
		||||
    OsmoBSC -> OsmoMGW [label="MGCP"];
 | 
			
		||||
    OsmoBTS -> OsmoMGW [label="RTP",dir=both];
 | 
			
		||||
    OsmoMGW -> OsmoMSC [label="RTP",dir=both];
 | 
			
		||||
    {rank=same OsmoBSC OsmoMGW}
 | 
			
		||||
    OsmoMGW [color=red];
 | 
			
		||||
}
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
[[fig-msc]]
 | 
			
		||||
.OsmoMGW used with OsmoMSC
 | 
			
		||||
[graphviz]
 | 
			
		||||
----
 | 
			
		||||
digraph G {
 | 
			
		||||
    rankdir = LR;
 | 
			
		||||
    BTS -> BSC [label="Abis"];
 | 
			
		||||
    BSC -> OsmoMSC [label="3GPP AoIP"];
 | 
			
		||||
    OsmoMSC -> OsmoMGW [label="MGCP"];
 | 
			
		||||
    BSC -> 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];
 | 
			
		||||
}
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
=== 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
 | 
			
		||||
 | 
			
		||||
Osmux is not yet supported in OsmoMGW.
 | 
			
		||||
 | 
			
		||||
At the moment (July 2018), OsmoMGW only implements RTP proxy / RTP bridge
 | 
			
		||||
type endpoints, to each of which two RTP connections can be established.
 | 
			
		||||
We are planning to add endpoint types for:
 | 
			
		||||
 | 
			
		||||
- classic E1/T1 timeslots (64kBps alaw/ulaw)
 | 
			
		||||
- classic E1/T1 16k sub-slots with TRAU frames for classic BTS support
 | 
			
		||||
- 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
 | 
			
		||||
							
								
								
									
										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 OsmoBTS 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>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										33
									
								
								doc/manuals/osmomgw-usermanual.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								doc/manuals/osmomgw-usermanual.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
: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_extensions.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/osmux/osmux.adoc[]
 | 
			
		||||
 | 
			
		||||
//include::{srcdir}/chapters/counters.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>
 | 
			
		||||
							
								
								
									
										1677
									
								
								doc/manuals/vty/mgw_vty_reference.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1677
									
								
								doc/manuals/vty/mgw_vty_reference.xml
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -92,8 +92,8 @@ fi
 | 
			
		||||
if test -n "$v"
 | 
			
		||||
then
 | 
			
		||||
    : # use $v
 | 
			
		||||
elif test -d ./.git \
 | 
			
		||||
    && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
 | 
			
		||||
elif
 | 
			
		||||
       v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
 | 
			
		||||
	  || git describe --abbrev=4 HEAD 2>/dev/null` \
 | 
			
		||||
    && case $v in
 | 
			
		||||
	 [0-9]*) ;;
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,17 @@ SUBDIRS = \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
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_endpoint_fsm.h \
 | 
			
		||||
	osmocom/mgcp_client/mgcp_client_fsm.h \
 | 
			
		||||
	osmocom/mgcp/mgcp.h \
 | 
			
		||||
	osmocom/mgcp/mgcp_common.h \
 | 
			
		||||
	osmocom/mgcp/mgcp_internal.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)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	legacy_mgcp \
 | 
			
		||||
	mgcp_client \
 | 
			
		||||
	mgcp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +0,0 @@
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	mgcp_transcode.h \
 | 
			
		||||
	vty.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
@@ -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
 | 
			
		||||
							
								
								
									
										12
									
								
								include/osmocom/mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								include/osmocom/mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	vty.h \
 | 
			
		||||
	mgcp_msg.h \
 | 
			
		||||
	mgcp_conn.h \
 | 
			
		||||
	mgcp_stat.h \
 | 
			
		||||
	mgcp_endp.h \
 | 
			
		||||
	mgcp_sdp.h \
 | 
			
		||||
	mgcp_codec.h \
 | 
			
		||||
	debug.h \
 | 
			
		||||
	iuup_cn_node.h \
 | 
			
		||||
	iuup_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,
 | 
			
		||||
	DIUUP,
 | 
			
		||||
	Debug_LastEntry,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct log_info log_info;
 | 
			
		||||
							
								
								
									
										47
									
								
								include/osmocom/mgcp/iuup_cn_node.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								include/osmocom/mgcp/iuup_cn_node.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
/* IuUP CN node, minimal implementation */
 | 
			
		||||
 | 
			
		||||
/*                                            _____IuUP_CN_____
 | 
			
		||||
 *                                            |               |
 | 
			
		||||
 * UE <--> RNC --PDU-> osmo_iuup_cn_rx_pdu() -+->           ---+-> rx_payload()
 | 
			
		||||
 *          |                                 |               |
 | 
			
		||||
 *          |  <-PDU-- tx_msg() <-------------+--           <-+--- osmo_iuup_cn_tx_payload()
 | 
			
		||||
 *                                            |               |
 | 
			
		||||
 *                                            -----------------
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
struct msgb;
 | 
			
		||||
 | 
			
		||||
typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv);
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_cn_cfg {
 | 
			
		||||
	void *node_priv;
 | 
			
		||||
 | 
			
		||||
	/* When the IuUP peer sent a voice packet, the clean RTP without the IuUP header is fed to this
 | 
			
		||||
	 * callback. */
 | 
			
		||||
	osmo_iuup_data_cb_t rx_payload;
 | 
			
		||||
 | 
			
		||||
	/* IuUP handler requests that a PDU shall be sent to the IuUP peer (e.g. the RNC).
 | 
			
		||||
	 * It is guaranteed that the msgb->dst pointer is preserved or copied from the msgb that
 | 
			
		||||
	 * originated the request. */
 | 
			
		||||
	osmo_iuup_data_cb_t tx_msg;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_cn {
 | 
			
		||||
	struct osmo_iuup_cn_cfg cfg;
 | 
			
		||||
	char *name;
 | 
			
		||||
	uint8_t next_frame_nr;
 | 
			
		||||
	int rtp_payload_type;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
bool osmo_iuup_cn_is_iuup_init(struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
 | 
			
		||||
				       const char *name_fmt, ...);
 | 
			
		||||
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn);
 | 
			
		||||
 | 
			
		||||
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload);
 | 
			
		||||
 | 
			
		||||
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu);
 | 
			
		||||
							
								
								
									
										117
									
								
								include/osmocom/mgcp/iuup_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								include/osmocom/mgcp/iuup_protocol.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
/* IuUP protocol handling, minimal implementation */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/endian.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
 | 
			
		||||
#define OSMO_IUUP_HEADROOM 32
 | 
			
		||||
 | 
			
		||||
enum osmo_iuup_pdu_type {
 | 
			
		||||
	OSMO_IUUP_PDU_DATA_WITH_CRC = 0,
 | 
			
		||||
	OSMO_IUUP_PDU_CONTROL_PROCEDURE = 14,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmo_iuup_acknack {
 | 
			
		||||
	OSMO_IUUP_ACKNACK_PROCEDURE = 0,
 | 
			
		||||
	OSMO_IUUP_ACKNACK_ACK = 1,
 | 
			
		||||
	OSMO_IUUP_ACKNACK_NACK = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmo_iuup_procedure {
 | 
			
		||||
	OSMO_IUUP_PROC_INITIALIZATION = 0,
 | 
			
		||||
	OSMO_IUUP_PROC_RATE_CONTROL = 1,
 | 
			
		||||
	OSMO_IUUP_PROC_TIME_ALIGNMENT = 2,
 | 
			
		||||
	OSMO_IUUP_PROC_ERROR_EVENT = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmo_iuup_frame_good {
 | 
			
		||||
	OSMO_IUUP_FRAME_GOOD = 0,
 | 
			
		||||
	OSMO_IUUP_FRAME_BAD = 1,
 | 
			
		||||
	OSMO_IUUP_FRAME_BAD_DUE_TO_RADIO = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_hdr_ctrl {
 | 
			
		||||
#if OSMO_IS_BIG_ENDIAN
 | 
			
		||||
	uint8_t pdu_type:4,
 | 
			
		||||
		ack_nack:2,
 | 
			
		||||
		frame_nr:2;
 | 
			
		||||
	uint8_t mode_version:4,
 | 
			
		||||
		procedure:4;
 | 
			
		||||
	uint8_t header_crc:6,
 | 
			
		||||
		payload_crc_hi:2;
 | 
			
		||||
	uint8_t payload_crc_lo;
 | 
			
		||||
	uint8_t payload[0];
 | 
			
		||||
#elif OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
	uint8_t frame_nr:2,
 | 
			
		||||
		ack_nack:2,
 | 
			
		||||
		pdu_type:4;
 | 
			
		||||
	uint8_t procedure:4,
 | 
			
		||||
		mode_version:4;
 | 
			
		||||
	uint8_t payload_crc_hi:2,
 | 
			
		||||
		header_crc:6;
 | 
			
		||||
	uint8_t payload_crc_lo;
 | 
			
		||||
	uint8_t payload[0];
 | 
			
		||||
#endif
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
union osmo_iuup_hdr_ctrl_payload {
 | 
			
		||||
	struct {
 | 
			
		||||
#if OSMO_IS_BIG_ENDIAN
 | 
			
		||||
	uint8_t spare:3,
 | 
			
		||||
		iptis_present:1,
 | 
			
		||||
		subflows:3,
 | 
			
		||||
		chain:1;
 | 
			
		||||
#elif OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
	uint8_t spare:3,
 | 
			
		||||
		iptis_present:1,
 | 
			
		||||
		subflows:3,
 | 
			
		||||
		chain:1;
 | 
			
		||||
#endif
 | 
			
		||||
	} initialization;
 | 
			
		||||
 | 
			
		||||
	struct {
 | 
			
		||||
#if OSMO_IS_BIG_ENDIAN
 | 
			
		||||
	uint8_t error_distance:2,
 | 
			
		||||
		error_cause:6;
 | 
			
		||||
#elif OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
	uint8_t error_cause:6,
 | 
			
		||||
		error_distance:2;
 | 
			
		||||
#endif
 | 
			
		||||
	} error_event;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern const struct value_string osmo_iuup_error_cause_names[];
 | 
			
		||||
static inline const char *osmo_iuup_error_cause_name(uint8_t val)
 | 
			
		||||
{ return get_value_string(osmo_iuup_error_cause_names, val); }
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_hdr_data {
 | 
			
		||||
#if OSMO_IS_BIG_ENDIAN
 | 
			
		||||
	uint8_t pdu_type:4,
 | 
			
		||||
		frame_nr:4;
 | 
			
		||||
	uint8_t frame_good:2,
 | 
			
		||||
		rfci:6;
 | 
			
		||||
	uint8_t header_crc:6,
 | 
			
		||||
		payload_crc_hi:2;
 | 
			
		||||
	uint8_t payload_crc_lo;
 | 
			
		||||
#elif OSMO_IS_LITTLE_ENDIAN
 | 
			
		||||
	uint8_t frame_nr:4,
 | 
			
		||||
		pdu_type:4;
 | 
			
		||||
	uint8_t rfci:6,
 | 
			
		||||
		frame_good:2;
 | 
			
		||||
	uint8_t payload_crc_hi:2,
 | 
			
		||||
		header_crc:6;
 | 
			
		||||
	uint8_t payload_crc_lo;
 | 
			
		||||
#endif
 | 
			
		||||
	uint8_t payload[0];
 | 
			
		||||
} __attribute__((packed));
 | 
			
		||||
 | 
			
		||||
int osmo_iuup_classify(bool log_errors,
 | 
			
		||||
		       const char *log_label,
 | 
			
		||||
		       struct msgb *pdu,
 | 
			
		||||
		       struct osmo_iuup_hdr_ctrl **is_ctrl,
 | 
			
		||||
		       struct osmo_iuup_hdr_data **is_data);
 | 
			
		||||
bool osmo_iuup_is_init(struct msgb *pdu);
 | 
			
		||||
void osmo_iuup_make_init_ack(struct msgb *ack);
 | 
			
		||||
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len);
 | 
			
		||||
@@ -20,46 +20,22 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef OPENBSC_MGCP_H
 | 
			
		||||
#define OPENBSC_MGCP_H
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.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>
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
@@ -98,37 +74,99 @@ typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
 | 
			
		||||
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_rtp_end *dst_end,
 | 
			
		||||
				     struct mgcp_rtp_end *src_end);
 | 
			
		||||
				     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,
 | 
			
		||||
				int *payload_type,
 | 
			
		||||
				const char**subtype_name,
 | 
			
		||||
				const char**fmtp_extra);
 | 
			
		||||
 | 
			
		||||
#define PORT_ALLOC_STATIC	0
 | 
			
		||||
#define PORT_ALLOC_DYNAMIC	1
 | 
			
		||||
				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 {
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	/* 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
 | 
			
		||||
 | 
			
		||||
/* 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_REJECTED_BY_POLICY,
 | 
			
		||||
	MGCP_CRCX_FAIL_NO_OSMUX,
 | 
			
		||||
	MGCP_CRCX_FAIL_INVALID_CONN_OPTIONS,
 | 
			
		||||
	MGCP_CRCX_FAIL_CODEC_NEGOTIATION,
 | 
			
		||||
	MGCP_CRCX_FAIL_BIND_PORT,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 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_REJECTED_BY_POLICY,
 | 
			
		||||
	MGCP_MDCX_DEFERRED_BY_POLICY
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Global MCGP DLCX related rate counters */
 | 
			
		||||
enum {
 | 
			
		||||
	MGCP_DLCX_SUCCESS,
 | 
			
		||||
	MGCP_DLCX_FAIL_WILDCARD,
 | 
			
		||||
	MGCP_DLCX_FAIL_NO_CONN,
 | 
			
		||||
	MGCP_DLCX_FAIL_INVALID_CALLID,
 | 
			
		||||
	MGCP_DLCX_FAIL_INVALID_CONNID,
 | 
			
		||||
	MGCP_DLCX_FAIL_UNHANDLED_PARAM,
 | 
			
		||||
	MGCP_DLCX_FAIL_REJECTED_BY_POLICY,
 | 
			
		||||
	MGCP_DLCX_DEFERRED_BY_POLICY,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_trunk_config {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
@@ -153,6 +191,7 @@ struct mgcp_trunk_config {
 | 
			
		||||
	/* RTP patching */
 | 
			
		||||
	int force_constant_ssrc; /* 0: don't, 1: once */
 | 
			
		||||
	int force_aligned_timing;
 | 
			
		||||
	bool rfc5993_hr_convert;
 | 
			
		||||
 | 
			
		||||
	/* spec handling */
 | 
			
		||||
	int force_realloc;
 | 
			
		||||
@@ -160,8 +199,22 @@ struct mgcp_trunk_config {
 | 
			
		||||
	/* 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;
 | 
			
		||||
	int vty_number_endpoints;
 | 
			
		||||
	struct mgcp_endpoint *endpoints;
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum mgcp_role {
 | 
			
		||||
@@ -169,35 +222,12 @@ enum mgcp_role {
 | 
			
		||||
	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;
 | 
			
		||||
@@ -206,12 +236,10 @@ struct mgcp_config {
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	int force_ptime;
 | 
			
		||||
 | 
			
		||||
	mgcp_change change_cb;
 | 
			
		||||
	mgcp_policy policy_cb;
 | 
			
		||||
@@ -226,10 +254,6 @@ struct mgcp_config {
 | 
			
		||||
	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 */
 | 
			
		||||
@@ -250,6 +274,11 @@ struct mgcp_config {
 | 
			
		||||
	 * 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* config management */
 | 
			
		||||
@@ -258,12 +287,6 @@ 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);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -271,29 +294,10 @@ void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval);
 | 
			
		||||
 */
 | 
			
		||||
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
 | 
			
		||||
							
								
								
									
										9
									
								
								include/osmocom/mgcp/mgcp_codec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								include/osmocom/mgcp/mgcp_codec.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
							
								
								
									
										107
									
								
								include/osmocom/mgcp/mgcp_common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								include/osmocom/mgcp/mgcp_common.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
/* 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/"
 | 
			
		||||
 | 
			
		||||
/* Maximal number of payload types / codecs that can be negotiated via SDP at
 | 
			
		||||
 * at once. */
 | 
			
		||||
#define MGCP_MAX_CODECS 10
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										80
									
								
								include/osmocom/mgcp/mgcp_conn.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								include/osmocom/mgcp/mgcp_conn.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,80 @@
 | 
			
		||||
/* 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_internal.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
/* 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."}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
							
								
								
									
										113
									
								
								include/osmocom/mgcp/mgcp_endp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								include/osmocom/mgcp/mgcp_endp.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
			
		||||
/* 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>
 | 
			
		||||
 | 
			
		||||
struct sockaddr_in;
 | 
			
		||||
struct mgcp_conn;
 | 
			
		||||
struct mgcp_conn_rtp;
 | 
			
		||||
struct mgcp_endpoint;
 | 
			
		||||
 | 
			
		||||
struct osmo_rtp_msg_ctx {
 | 
			
		||||
	int proto;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_src;
 | 
			
		||||
	struct sockaddr_in *from_addr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define OSMO_RTP_MSG_CTX(MSGB) (*(struct osmo_rtp_msg_ctx**)&((MSGB)->dst))
 | 
			
		||||
 | 
			
		||||
#define LOG_ENDP(endp, level, fmt, args...) \
 | 
			
		||||
	LOGP(DRTP, level, "%x@ " fmt, ENDPOINT_NUMBER(endp), ## args)
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! static MGCP endpoint typeset (pre-initalized, read-only) */
 | 
			
		||||
extern const struct mgcp_endpoint_typeset ep_typeset;
 | 
			
		||||
 | 
			
		||||
/*! MGCP endpoint model */
 | 
			
		||||
struct mgcp_endpoint {
 | 
			
		||||
 | 
			
		||||
	/*! 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 MGW configuration */
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
 | 
			
		||||
	/*! Backpointer to the Trunk specific configuration */
 | 
			
		||||
	struct mgcp_trunk_config *tcfg;
 | 
			
		||||
 | 
			
		||||
	/*! 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;
 | 
			
		||||
 | 
			
		||||
	/*! Memorize if this endpoint was choosen by the MGW (wildcarded, true)
 | 
			
		||||
	 *   or if the user has choosen the particular endpoint explicitly. */
 | 
			
		||||
	bool wildcarded_req;
 | 
			
		||||
 | 
			
		||||
	/*! MGCP_X_OSMO_IGN_* flags from 'X-Osmo-IGN:' header */
 | 
			
		||||
	uint32_t x_osmo_ign;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! Extract endpoint number for a given endpoint */
 | 
			
		||||
#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints))
 | 
			
		||||
 | 
			
		||||
void mgcp_endp_release(struct mgcp_endpoint *endp);
 | 
			
		||||
							
								
								
									
										365
									
								
								include/osmocom/mgcp/mgcp_internal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								include/osmocom/mgcp/mgcp_internal.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,365 @@
 | 
			
		||||
/* 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 <inttypes.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/counter.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
 | 
			
		||||
#define CI_UNUSED 0
 | 
			
		||||
 | 
			
		||||
/* FIXME: This this is only needed to compile the currently
 | 
			
		||||
 * broken OSMUX support. Remove when fixed */
 | 
			
		||||
#define CONN_ID_BTS "0"
 | 
			
		||||
#define CONN_ID_NET "1"
 | 
			
		||||
 | 
			
		||||
#define LOG_CONN(conn, level, fmt, args...) \
 | 
			
		||||
	LOGP(DRTP, level, "(%d@ I:%s) " fmt, \
 | 
			
		||||
	     ENDPOINT_NUMBER((conn)->endp), (conn)->id, ## args)
 | 
			
		||||
 | 
			
		||||
#define LOG_CONN_RTP(conn_rtp, level, fmt, args...) \
 | 
			
		||||
	LOG_CONN(conn_rtp->conn, level, fmt, ## args)
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	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? */
 | 
			
		||||
		int 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;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	bool param_present;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 'mgcp_rtp_end': basically a wrapper around the RTP+RTCP ports */
 | 
			
		||||
struct mgcp_rtp_end {
 | 
			
		||||
	/* local IP address of the RTP socket */
 | 
			
		||||
	struct in_addr 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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 sockaddr_in forward;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mgcp_lco {
 | 
			
		||||
	char *string;
 | 
			
		||||
	char *codec;
 | 
			
		||||
	int pkt_period_min; /* time in ms */
 | 
			
		||||
	int pkt_period_max; /* time in ms */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* 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,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
struct mgcp_conn;
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
 | 
			
		||||
	struct osmo_iuup_cn *iuup;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! 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 (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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
 | 
			
		||||
struct mgcp_endpoint_type;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Internal structure while parsing a request
 | 
			
		||||
 */
 | 
			
		||||
struct mgcp_parse_data {
 | 
			
		||||
	struct mgcp_config *cfg;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	char *trans;
 | 
			
		||||
	char *save;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *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_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);
 | 
			
		||||
 | 
			
		||||
/* 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);
 | 
			
		||||
 | 
			
		||||
char *get_lco_identifier(const char *options);
 | 
			
		||||
int check_local_cx_options(void *ctx, const char *options);
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
/* 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(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);
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
void mgcp_get_local_addr(char *addr, struct mgcp_conn_rtp *conn);
 | 
			
		||||
void mgcp_conn_watchdog_kick(struct mgcp_conn *conn);
 | 
			
		||||
 | 
			
		||||
#define LOGPENDP(endp, cat, level, fmt, args...) \
 | 
			
		||||
LOGP(cat, level, "endpoint:0x%x " fmt, \
 | 
			
		||||
     endp ? ENDPOINT_NUMBER(endp) : -1, \
 | 
			
		||||
     ## args)
 | 
			
		||||
 | 
			
		||||
#define LOGPCONN(conn, cat, level, fmt, args...) \
 | 
			
		||||
LOGPENDP((conn)->endp, cat, level, "CI:%s " fmt, \
 | 
			
		||||
         (conn)->id, \
 | 
			
		||||
         ## args)
 | 
			
		||||
 | 
			
		||||
void mgcp_patch_and_count(struct mgcp_endpoint *endp,
 | 
			
		||||
			  struct mgcp_rtp_state *state,
 | 
			
		||||
			  struct mgcp_rtp_end *rtp_end,
 | 
			
		||||
			  struct sockaddr_in *addr, struct msgb *msg);
 | 
			
		||||
							
								
								
									
										56
									
								
								include/osmocom/mgcp/mgcp_msg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								include/osmocom/mgcp/mgcp_msg.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
/* 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>
 | 
			
		||||
 | 
			
		||||
struct mgcp_conn;
 | 
			
		||||
struct mgcp_parse_data;
 | 
			
		||||
struct mgcp_endpoint;
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
int mgcp_check_param(const struct mgcp_endpoint *endp, 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))
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
							
								
								
									
										36
									
								
								include/osmocom/mgcp/mgcp_stat.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								include/osmocom/mgcp/mgcp_stat.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
/* 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 <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#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 *);
 | 
			
		||||
							
								
								
									
										39
									
								
								include/osmocom/mgcp/osmux.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								include/osmocom/mgcp/osmux.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#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 in_addr *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,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										8
									
								
								include/osmocom/mgcp/vty.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								include/osmocom/mgcp/vty.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
 | 
			
		||||
enum mgcp_vty_node {
 | 
			
		||||
	MGCP_NODE = _LAST_OSMOVTY_NODE + 1,
 | 
			
		||||
	TRUNK_NODE,
 | 
			
		||||
};
 | 
			
		||||
@@ -1,3 +1,13 @@
 | 
			
		||||
BUILT_SOURCES = \
 | 
			
		||||
	mgcp_common.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	mgcp_client_internal.h \
 | 
			
		||||
	$(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
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_common.h>
 | 
			
		||||
 | 
			
		||||
/* See also: RFC 3435, chapter 3.5 Transmission over UDP */
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_ADDR_DEFAULT "0.0.0.0"
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 0
 | 
			
		||||
#define MGCP_CLIENT_LOCAL_PORT_DEFAULT 2727
 | 
			
		||||
#define MGCP_CLIENT_REMOTE_ADDR_DEFAULT "127.0.0.1"
 | 
			
		||||
#define MGCP_CLIENT_REMOTE_PORT_DEFAULT 2427
 | 
			
		||||
 | 
			
		||||
#define MGCP_CLIENT_MGW_STR "Configure MGCP connection to Media Gateway\n"
 | 
			
		||||
 | 
			
		||||
struct msgb;
 | 
			
		||||
struct vty;
 | 
			
		||||
struct mgcp_client;
 | 
			
		||||
@@ -16,23 +22,104 @@ struct mgcp_client_conf {
 | 
			
		||||
	int local_port;
 | 
			
		||||
	const char *remote_addr;
 | 
			
		||||
	int remote_port;
 | 
			
		||||
	uint16_t first_endpoint;
 | 
			
		||||
	uint16_t last_endpoint;
 | 
			
		||||
	uint16_t bts_base;
 | 
			
		||||
 | 
			
		||||
	/* By default, we are always addressing the MGW with e.g. 'rtpbridge/123@mgw'.
 | 
			
		||||
	 * If this is nonempty, the contained name will be used instead of 'mgw'. */
 | 
			
		||||
	char endpoint_domain_name[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
       int response_code;
 | 
			
		||||
       mgcp_trans_id_t trans_id;
 | 
			
		||||
       const char *comment;
 | 
			
		||||
	int response_code;
 | 
			
		||||
	mgcp_trans_id_t trans_id;
 | 
			
		||||
	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 {
 | 
			
		||||
	char *body;
 | 
			
		||||
	struct mgcp_response_head head;
 | 
			
		||||
	uint16_t audio_port;
 | 
			
		||||
	char audio_ip[INET_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);
 | 
			
		||||
@@ -48,8 +135,8 @@ const char *mgcp_client_remote_addr_str(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);
 | 
			
		||||
 | 
			
		||||
int mgcp_client_next_endpoint(struct mgcp_client *client);
 | 
			
		||||
void mgcp_client_release_endpoint(uint16_t id, struct mgcp_client *client);
 | 
			
		||||
const char *mgcp_client_endpoint_domain(const struct mgcp_client *mgcp);
 | 
			
		||||
const char *mgcp_client_rtpbridge_wildcard(const struct mgcp_client *mgcp);
 | 
			
		||||
 | 
			
		||||
/* Invoked when an MGCP response is received or sending failed.  When the
 | 
			
		||||
 * response is passed as NULL, this indicates failure during transmission. */
 | 
			
		||||
@@ -58,16 +145,21 @@ int mgcp_response_parse_params(struct mgcp_response *r);
 | 
			
		||||
 | 
			
		||||
int mgcp_client_tx(struct mgcp_client *mgcp, struct msgb *msg,
 | 
			
		||||
		   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;
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_crcx(struct mgcp_client *mgcp,
 | 
			
		||||
			   uint16_t rtp_endpoint, unsigned int call_id,
 | 
			
		||||
			   enum mgcp_connection_mode mode);
 | 
			
		||||
struct msgb *mgcp_msg_gen(struct mgcp_client *mgcp, struct mgcp_msg *mgcp_msg);
 | 
			
		||||
mgcp_trans_id_t mgcp_msg_trans_id(struct msgb *msg);
 | 
			
		||||
 | 
			
		||||
struct msgb *mgcp_msg_mdcx(struct mgcp_client *mgcp,
 | 
			
		||||
			   uint16_t rtp_endpoint, const char *rtp_conn_addr,
 | 
			
		||||
			   uint16_t rtp_port, enum mgcp_connection_mode mode);
 | 
			
		||||
extern const struct value_string mgcp_client_connection_mode_strs[];
 | 
			
		||||
static inline const char *mgcp_client_cmode_name(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,
 | 
			
		||||
			   unsigned int call_id);
 | 
			
		||||
enum mgcp_codecs map_str_to_codec(const char *str);
 | 
			
		||||
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);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								include/osmocom/mgcp_client/mgcp_client_endpoint_fsm.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
/* 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);
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
/*! 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);
 | 
			
		||||
 | 
			
		||||
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); }
 | 
			
		||||
							
								
								
									
										73
									
								
								include/osmocom/mgcp_client/mgcp_client_fsm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								include/osmocom/mgcp_client/mgcp_client_fsm.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
#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[INET_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);
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_conn_peer_name(const struct mgcp_conn_peer *info);
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/write_queue.h>
 | 
			
		||||
 | 
			
		||||
#define MSGB_CB_MGCP_TRANS_ID 0
 | 
			
		||||
 | 
			
		||||
struct mgcp_client {
 | 
			
		||||
 
 | 
			
		||||
@@ -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}/
 | 
			
		||||
@@ -16,13 +16,13 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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",
 | 
			
		||||
               "doc/examples/osmo-bsc_mgcp/mgcp.cfg"]
 | 
			
		||||
vty_command = ["./src/osmo-mgw/osmo-mgw", "-c",
 | 
			
		||||
               "doc/examples/osmo-mgw/osmo-mgw.cfg"]
 | 
			
		||||
 | 
			
		||||
vty_app = apps[0]
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,11 @@ AM_LDFLAGS = \
 | 
			
		||||
 | 
			
		||||
# Libraries
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	libosmo-legacy-mgcp \
 | 
			
		||||
	libosmo-mgcp-client \
 | 
			
		||||
	libosmo-mgcp \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# Programs
 | 
			
		||||
SUBDIRS += \
 | 
			
		||||
	osmo-bsc_mgcp \
 | 
			
		||||
	osmo-mgw \
 | 
			
		||||
	$(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;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_builddir)/include \
 | 
			
		||||
	-I$(top_builddir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
@@ -8,21 +9,18 @@ AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(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
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=1:0:1
 | 
			
		||||
# This is not at all related to the release version, but a range of supported
 | 
			
		||||
# API versions. Read TODO_RELEASE in the source tree's root!
 | 
			
		||||
MGCP_CLIENT_LIBVERSION=6:0:0
 | 
			
		||||
 | 
			
		||||
lib_LTLIBRARIES = \
 | 
			
		||||
	libosmo-mgcp-client.la \
 | 
			
		||||
@@ -31,7 +29,8 @@ lib_LTLIBRARIES = \
 | 
			
		||||
libosmo_mgcp_client_la_SOURCES = \
 | 
			
		||||
	mgcp_client.c \
 | 
			
		||||
	mgcp_client_vty.c \
 | 
			
		||||
	../libosmo-legacy-mgcp/mgcp_common.c \
 | 
			
		||||
	mgcp_client_fsm.c \
 | 
			
		||||
	mgcp_client_endpoint_fsm.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
libosmo_mgcp_client_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(MGCP_CLIENT_LIBVERSION)
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										886
									
								
								src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										886
									
								
								src/libosmo-mgcp-client/mgcp_client_endpoint_fsm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,886 @@
 | 
			
		||||
/* FSM to manage multiple connections of an MGW endpoint
 | 
			
		||||
 *
 | 
			
		||||
 * (C) 2018-2019 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Hofmeyr <neels@hofmeyr.de>
 | 
			
		||||
 *
 | 
			
		||||
 * 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 <sys/socket.h>
 | 
			
		||||
#include <netinet/in.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/fsm.h>
 | 
			
		||||
#include <osmocom/core/byteswap.h>
 | 
			
		||||
#include <osmocom/core/tdef.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
 | 
			
		||||
 | 
			
		||||
#define LOG_CI(ci, level, fmt, args...) do { \
 | 
			
		||||
	if (!ci || !ci->ep) \
 | 
			
		||||
		LOGP(DLGLOBAL, level, "(unknown MGW endpoint) " fmt, ## args); \
 | 
			
		||||
	else \
 | 
			
		||||
		LOG_MGCPC_EP(ci->ep, level, "CI[%d] %s%s%s: " fmt, \
 | 
			
		||||
			(int)(ci - ci->ep->ci), \
 | 
			
		||||
			ci->label ? : "-", \
 | 
			
		||||
			ci->mgcp_ci_str[0] ? " CI=" : "", \
 | 
			
		||||
			ci->mgcp_ci_str[0] ? ci->mgcp_ci_str : "", \
 | 
			
		||||
			## args); \
 | 
			
		||||
	} while(0)
 | 
			
		||||
 | 
			
		||||
#define LOG_CI_VERB(ci, level, fmt, args...) do { \
 | 
			
		||||
	if (ci->verb_info.addr[0]) \
 | 
			
		||||
		LOG_CI(ci, level, "%s %s:%u: " fmt, \
 | 
			
		||||
			osmo_mgcp_verb_name(ci->verb), ci->verb_info.addr, ci->verb_info.port, \
 | 
			
		||||
			## args); \
 | 
			
		||||
	else \
 | 
			
		||||
		LOG_CI(ci, level, "%s: " fmt, \
 | 
			
		||||
			osmo_mgcp_verb_name(ci->verb), \
 | 
			
		||||
			## args); \
 | 
			
		||||
	} while(0)
 | 
			
		||||
 | 
			
		||||
enum osmo_mgcpc_ep_fsm_state {
 | 
			
		||||
	OSMO_MGCPC_EP_ST_UNUSED = 0,
 | 
			
		||||
	OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE,
 | 
			
		||||
	OSMO_MGCPC_EP_ST_IN_USE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum osmo_mgcpc_ep_fsm_event {
 | 
			
		||||
	_OSMO_MGCPC_EP_EV_LAST = 0,
 | 
			
		||||
	/* and MGW response events are allocated dynamically */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define FIRST_CI_EVENT (_OSMO_MGCPC_EP_EV_LAST + (_OSMO_MGCPC_EP_EV_LAST & 1)) /* rounded up to even nr */
 | 
			
		||||
#define USABLE_CI ((32 - FIRST_CI_EVENT)/2)
 | 
			
		||||
#define EV_TO_CI_IDX(event) ((event - FIRST_CI_EVENT) / 2)
 | 
			
		||||
 | 
			
		||||
#define CI_EV_SUCCESS(ci) (FIRST_CI_EVENT + (((ci) - ci->ep->ci) * 2))
 | 
			
		||||
#define CI_EV_FAILURE(ci) (CI_EV_SUCCESS(ci) + 1)
 | 
			
		||||
 | 
			
		||||
static struct osmo_fsm osmo_mgcpc_ep_fsm;
 | 
			
		||||
 | 
			
		||||
/*! One connection on an endpoint, corresponding to a connection identifier (CI) as returned by the MGW.
 | 
			
		||||
 * An endpoint has a fixed number of slots of these, which may or may not be in use.
 | 
			
		||||
 */
 | 
			
		||||
struct osmo_mgcpc_ep_ci {
 | 
			
		||||
	struct osmo_mgcpc_ep *ep;
 | 
			
		||||
 | 
			
		||||
	bool occupied;
 | 
			
		||||
	char label[64];
 | 
			
		||||
	struct osmo_fsm_inst *mgcp_client_fi;
 | 
			
		||||
 | 
			
		||||
	bool pending;
 | 
			
		||||
	bool sent;
 | 
			
		||||
	enum mgcp_verb verb;
 | 
			
		||||
	struct mgcp_conn_peer verb_info;
 | 
			
		||||
	struct osmo_fsm_inst *notify;
 | 
			
		||||
	uint32_t notify_success;
 | 
			
		||||
	uint32_t notify_failure;
 | 
			
		||||
	void *notify_data;
 | 
			
		||||
 | 
			
		||||
	bool got_port_info;
 | 
			
		||||
	struct mgcp_conn_peer rtp_info;
 | 
			
		||||
	char mgcp_ci_str[MGCP_CONN_ID_LENGTH];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! An MGW endpoint with N connections, like "rtpbridge/23@mgw". */
 | 
			
		||||
struct osmo_mgcpc_ep {
 | 
			
		||||
	/*! MGCP client connection to the MGW. */
 | 
			
		||||
	struct mgcp_client *mgcp_client;
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
 | 
			
		||||
	/*! Endpoint string; at first this might be a wildcard, and upon the first CRCX OK response, this will reflect
 | 
			
		||||
	 * the endpoint name returned by the MGW. */
 | 
			
		||||
	char endpoint[MGCP_ENDPOINT_MAXLEN];
 | 
			
		||||
 | 
			
		||||
	/*! Timeout definitions used for this endpoint, see osmo_mgcpc_ep_fsm_timeouts. */
 | 
			
		||||
	const struct osmo_tdef *T_defs;
 | 
			
		||||
 | 
			
		||||
	/*! True as soon as the first CRCX OK is received. The endpoint name may be determined by the first CRCX
 | 
			
		||||
	 * response, so to dispatch any other messages, the FSM instance *must* wait for the first CRCX OK to arrive
 | 
			
		||||
	 * first. Once the endpoint name is pinpointed, any amount of operations may be dispatched concurrently. */
 | 
			
		||||
	bool first_crcx_complete;
 | 
			
		||||
 | 
			
		||||
	/*! Endpoint connection slots. Note that each connection has its own set of FSM event numbers to signal success
 | 
			
		||||
	 * and failure, depending on its index within this array. See CI_EV_SUCCESS and CI_EV_FAILURE. */
 | 
			
		||||
	struct osmo_mgcpc_ep_ci ci[USABLE_CI];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct value_string osmo_mgcp_verb_names[] = {
 | 
			
		||||
	{ MGCP_VERB_CRCX, "CRCX" },
 | 
			
		||||
	{ MGCP_VERB_MDCX, "MDCX" },
 | 
			
		||||
	{ MGCP_VERB_DLCX, "DLCX" },
 | 
			
		||||
	{ MGCP_VERB_AUEP, "AUEP" },
 | 
			
		||||
	{ MGCP_VERB_RSIP, "RSIP" },
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void osmo_mgcpc_ep_count(struct osmo_mgcpc_ep *ep, int *occupied, int *pending_not_sent,
 | 
			
		||||
				int *waiting_for_response);
 | 
			
		||||
 | 
			
		||||
static struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_check_ci(struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	if (!ci)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	if (!ci->ep)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	if (ci < ci->ep->ci || ci >= &ci->ep->ci[USABLE_CI])
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return ci;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_for_event(struct osmo_mgcpc_ep *ep, uint32_t event)
 | 
			
		||||
{
 | 
			
		||||
	int idx;
 | 
			
		||||
	if (event < FIRST_CI_EVENT)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	idx = EV_TO_CI_IDX(event);
 | 
			
		||||
	if (idx >= sizeof(ep->ci))
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return osmo_mgcpc_ep_check_ci(&ep->ci[idx]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_ep_name(const struct osmo_mgcpc_ep *ep)
 | 
			
		||||
{
 | 
			
		||||
	if (!ep)
 | 
			
		||||
		return "NULL";
 | 
			
		||||
	if (ep->endpoint[0])
 | 
			
		||||
		return ep->endpoint;
 | 
			
		||||
	return osmo_fsm_inst_name(ep->fi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *mgcp_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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_ep_ci_name(const struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_conn_peer *rtp_info;
 | 
			
		||||
 | 
			
		||||
	if (!ci)
 | 
			
		||||
		return "NULL";
 | 
			
		||||
 | 
			
		||||
	rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
 | 
			
		||||
 | 
			
		||||
	if (rtp_info)
 | 
			
		||||
		return mgcp_conn_peer_name(rtp_info);
 | 
			
		||||
	return osmo_mgcpc_ep_name(ci->ep);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *osmo_mgcpc_ep_ci_id(const struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	if (!ci || !ci->mgcp_ci_str[0])
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return ci->mgcp_ci_str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct value_string osmo_mgcpc_ep_fsm_event_names[33] = {};
 | 
			
		||||
 | 
			
		||||
static char osmo_mgcpc_ep_fsm_event_name_bufs[32][32] = {};
 | 
			
		||||
 | 
			
		||||
static void fill_event_names()
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	for (i = 0; i < (ARRAY_SIZE(osmo_mgcpc_ep_fsm_event_names) - 1); i++) {
 | 
			
		||||
		if (i < _OSMO_MGCPC_EP_EV_LAST)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (i < FIRST_CI_EVENT || EV_TO_CI_IDX(i) > USABLE_CI) {
 | 
			
		||||
			osmo_mgcpc_ep_fsm_event_names[i] = (struct value_string){i, "Unused"};
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		snprintf(osmo_mgcpc_ep_fsm_event_name_bufs[i], sizeof(osmo_mgcpc_ep_fsm_event_name_bufs[i]),
 | 
			
		||||
			 "MGW Response for CI #%d", EV_TO_CI_IDX(i));
 | 
			
		||||
		osmo_mgcpc_ep_fsm_event_names[i] = (struct value_string){i, osmo_mgcpc_ep_fsm_event_name_bufs[i]};
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* T_defs is used to obtain an (Osmocom specific) T2427001: timeout for an MGCP response (note, 2427 corresponds to the
 | 
			
		||||
 * default MGCP port in osmo-mgw). */
 | 
			
		||||
static __attribute__((constructor)) void osmo_mgcpc_ep_fsm_init()
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(osmo_fsm_register(&osmo_mgcpc_ep_fsm) == 0);
 | 
			
		||||
	fill_event_names();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct osmo_mgcpc_ep *osmo_mgcpc_ep_fi_mgwep(struct osmo_fsm_inst *fi)
 | 
			
		||||
{
 | 
			
		||||
	OSMO_ASSERT(fi);
 | 
			
		||||
	OSMO_ASSERT(fi->fsm == &osmo_mgcpc_ep_fsm);
 | 
			
		||||
	OSMO_ASSERT(fi->priv);
 | 
			
		||||
	return fi->priv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Allocate an osmo_mgcpc_ep FSM.
 | 
			
		||||
 * MGCP messages to set up the endpoint will be sent on the given mgcp_client, as soon as the first
 | 
			
		||||
 * osmo_mgcpc_ep_ci_request() is invoked.
 | 
			
		||||
 *
 | 
			
		||||
 * A typical sequence of events would be:
 | 
			
		||||
 *
 | 
			
		||||
 *    ep = osmo_mgcpc_ep_alloc(..., mgcp_client_rtpbridge_wildcard(client));
 | 
			
		||||
 *    ci_to_ran = osmo_mgcpc_ep_ci_add(ep);
 | 
			
		||||
 *    osmo_mgcpc_ep_ci_request(ci_to_ran, MGCP_VERB_CRCX, verb_info,
 | 
			
		||||
 *                             my_call_fsm, MY_EVENT_MGCP_OK, MY_EVENT_MGCP_FAIL);
 | 
			
		||||
 *    ci_to_cn = osmo_mgcpc_ep_ci_add(ep);
 | 
			
		||||
 *    osmo_mgcpc_ep_ci_request(ci_to_cn, MGCP_VERB_CRCX, verb_info,
 | 
			
		||||
 *                             my_call_fsm, MY_EVENT_MGCP_OK, MY_EVENT_MGCP_FAIL);
 | 
			
		||||
 *    ...
 | 
			
		||||
 *    osmo_mgcpc_ep_ci_request(ci_to_ran, MGCP_VERB_MDCX, ...);
 | 
			
		||||
 *    ...
 | 
			
		||||
 *    osmo_mgcpc_ep_clear(ep);
 | 
			
		||||
 *    ep = NULL;
 | 
			
		||||
 *
 | 
			
		||||
 * \param parent  Parent FSM.
 | 
			
		||||
 * \param parent_term_event  Event to dispatch to the parent on termination of this FSM instance.
 | 
			
		||||
 * \param mgcp_client  Connection to the MGW.
 | 
			
		||||
 * \param T_defs  Timeout definitions to be used for FSM states, see osmo_mgcpc_ep_fsm_timeouts.
 | 
			
		||||
 * \param fsm_id  FSM instance ID.
 | 
			
		||||
 * \param endpoint_str_fmt  The endpoint string format to send to the MGW upon the first CRCX.
 | 
			
		||||
 *                          See mgcp_client_rtpbridge_wildcard() for "rtpbridge" endpoints.
 | 
			
		||||
 */
 | 
			
		||||
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, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (!mgcp_client)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	fi = osmo_fsm_inst_alloc_child(&osmo_mgcpc_ep_fsm, parent, parent_term_event);
 | 
			
		||||
	OSMO_ASSERT(fi);
 | 
			
		||||
 | 
			
		||||
	osmo_fsm_inst_update_id(fi, fsm_id);
 | 
			
		||||
 | 
			
		||||
	ep = talloc_zero(fi, struct osmo_mgcpc_ep);
 | 
			
		||||
	OSMO_ASSERT(ep);
 | 
			
		||||
 | 
			
		||||
	*ep = (struct osmo_mgcpc_ep){
 | 
			
		||||
		.mgcp_client = mgcp_client,
 | 
			
		||||
		.fi = fi,
 | 
			
		||||
		.T_defs = T_defs,
 | 
			
		||||
	};
 | 
			
		||||
	fi->priv = ep;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, endpoint_str_fmt);
 | 
			
		||||
	rc = vsnprintf(ep->endpoint, sizeof(ep->endpoint), endpoint_str_fmt ? : "", ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	if (rc <= 0 || rc >= sizeof(ep->endpoint)) {
 | 
			
		||||
		LOG_MGCPC_EP(ep, LOGL_ERROR, "Endpoint name too long or too short: %s\n",
 | 
			
		||||
			  ep->endpoint);
 | 
			
		||||
		osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_ERROR, 0);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ep;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Add a connection to an endpoint.
 | 
			
		||||
 * Allocate a connection identifier slot in the osmo_mgcpc_ep instance, do not yet dispatch a CRCX.
 | 
			
		||||
 * The CRCX is dispatched only upon the first osmo_mgcpc_ep_ci_request().
 | 
			
		||||
 * \param ep  Parent endpoint instance.
 | 
			
		||||
 * \param label_fmt  Label for logging.
 | 
			
		||||
 */
 | 
			
		||||
struct osmo_mgcpc_ep_ci *osmo_mgcpc_ep_ci_add(struct osmo_mgcpc_ep *ep,
 | 
			
		||||
					      const char *label_fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	int i;
 | 
			
		||||
	struct osmo_mgcpc_ep_ci *ci;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < USABLE_CI; i++) {
 | 
			
		||||
		ci = &ep->ci[i];
 | 
			
		||||
 | 
			
		||||
		if (ci->occupied || ci->mgcp_client_fi)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		*ci = (struct osmo_mgcpc_ep_ci){
 | 
			
		||||
			.ep = ep,
 | 
			
		||||
			.occupied = true,
 | 
			
		||||
		};
 | 
			
		||||
		if (label_fmt) {
 | 
			
		||||
			va_start(ap, label_fmt);
 | 
			
		||||
			vsnprintf(ci->label, sizeof(ci->label), label_fmt, ap);
 | 
			
		||||
			va_end(ap);
 | 
			
		||||
		}
 | 
			
		||||
		return ci;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG_MGCPC_EP(ep, LOGL_ERROR,
 | 
			
		||||
		  "Cannot allocate another endpoint, all "
 | 
			
		||||
		  OSMO_STRINGIFY_VAL(USABLE_CI) " are in use\n");
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_inst *fi);
 | 
			
		||||
 | 
			
		||||
static void on_failure(struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_fsm_inst *notify = ci->notify;
 | 
			
		||||
	uint32_t notify_failure = ci->notify_failure;
 | 
			
		||||
	void *notify_data = ci->notify_data;
 | 
			
		||||
 | 
			
		||||
	if (!ci->occupied)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	*ci = (struct osmo_mgcpc_ep_ci){
 | 
			
		||||
		.ep = ci->ep,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/* If this check has terminated the FSM instance, don't fire any more events to prevent use-after-free problems.
 | 
			
		||||
	 * The endpoint FSM does dispatch a term event to its parent, and everything should be cleaned like that. */
 | 
			
		||||
	if (!osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (notify)
 | 
			
		||||
		osmo_fsm_inst_dispatch(notify, notify_failure, notify_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int update_endpoint_name(struct osmo_mgcpc_ep_ci *ci, const char *new_endpoint_name)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = ci->ep;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (!strcmp(ep->endpoint, new_endpoint_name)) {
 | 
			
		||||
		/* Same endpoint name, nothing to do. */
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* The endpoint name should only change on the very first CRCX response. */
 | 
			
		||||
	if (ep->first_crcx_complete) {
 | 
			
		||||
		LOG_CI(ci, LOGL_ERROR, "Reponse returned mismatching endpoint name."
 | 
			
		||||
		       " This is endpoint %s, instead received %s\n",
 | 
			
		||||
		       ep->endpoint, new_endpoint_name);
 | 
			
		||||
		on_failure(ci);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* This is the first CRCX response, update endpoint name. */
 | 
			
		||||
	rc = OSMO_STRLCPY_ARRAY(ep->endpoint, new_endpoint_name);
 | 
			
		||||
	if (rc <= 0 || rc >= sizeof(ep->endpoint)) {
 | 
			
		||||
		LOG_CI(ci, LOGL_ERROR, "Unable to copy endpoint name %s\n", osmo_quote_str(new_endpoint_name, -1));
 | 
			
		||||
		osmo_mgcpc_ep_ci_dlcx(ci);
 | 
			
		||||
		on_failure(ci);
 | 
			
		||||
		return -ENOSPC;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Make sure already pending requests use this updated endpoint name. */
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
 | 
			
		||||
		struct osmo_mgcpc_ep_ci *other_ci = &ep->ci[i];
 | 
			
		||||
		if (!other_ci->occupied)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (!other_ci->pending)
 | 
			
		||||
			continue;
 | 
			
		||||
		if (other_ci->sent)
 | 
			
		||||
			continue;
 | 
			
		||||
		OSMO_STRLCPY_ARRAY(other_ci->verb_info.endpoint, ep->endpoint);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void on_success(struct osmo_mgcpc_ep_ci *ci, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn_peer *rtp_info;
 | 
			
		||||
 | 
			
		||||
	if (!ci->occupied)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	ci->pending = false;
 | 
			
		||||
 | 
			
		||||
	switch (ci->verb) {
 | 
			
		||||
	case MGCP_VERB_CRCX:
 | 
			
		||||
		/* If we sent a wildcarded endpoint name on CRCX, we need to store the resulting endpoint
 | 
			
		||||
		 * name here. Also, we receive the MGW's RTP port information. */
 | 
			
		||||
		rtp_info = data;
 | 
			
		||||
		OSMO_ASSERT(rtp_info);
 | 
			
		||||
		ci->got_port_info = true;
 | 
			
		||||
		ci->rtp_info = *rtp_info;
 | 
			
		||||
		osmo_strlcpy(ci->mgcp_ci_str, mgcp_conn_get_ci(ci->mgcp_client_fi),
 | 
			
		||||
			sizeof(ci->mgcp_ci_str));
 | 
			
		||||
		if (rtp_info->endpoint[0]) {
 | 
			
		||||
			/* On errors, this instance might already be deallocated. Make sure to not access anything after
 | 
			
		||||
			 * error. */
 | 
			
		||||
			if (update_endpoint_name(ci, rtp_info->endpoint))
 | 
			
		||||
				return;
 | 
			
		||||
		}
 | 
			
		||||
		ci->ep->first_crcx_complete = true;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG_CI(ci, LOGL_DEBUG, "received successful response to %s: RTP=%s%s\n",
 | 
			
		||||
	       osmo_mgcp_verb_name(ci->verb),
 | 
			
		||||
	       mgcp_conn_peer_name(ci->got_port_info? &ci->rtp_info : NULL),
 | 
			
		||||
	       ci->notify ? "" : " (not sending a notification)");
 | 
			
		||||
 | 
			
		||||
	if (ci->notify)
 | 
			
		||||
		osmo_fsm_inst_dispatch(ci->notify, ci->notify_success, ci->notify_data);
 | 
			
		||||
 | 
			
		||||
	osmo_mgcpc_ep_fsm_check_state_chg_after_response(ci->ep->fi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
 | 
			
		||||
const struct mgcp_conn_peer *osmo_mgcpc_ep_ci_get_rtp_info(const struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	ci = osmo_mgcpc_ep_check_ci((struct osmo_mgcpc_ep_ci*)ci);
 | 
			
		||||
	if (!ci)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	if (!ci->got_port_info)
 | 
			
		||||
		return NULL;
 | 
			
		||||
	return &ci->rtp_info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Return the MGW's RTP port information for this connection, as returned by the last CRCX/MDCX OK message. */
 | 
			
		||||
bool osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(const struct osmo_mgcpc_ep_ci *ci, struct sockaddr_storage *dest)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_conn_peer *rtp_info;
 | 
			
		||||
	struct sockaddr_in *sin;
 | 
			
		||||
 | 
			
		||||
	rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
 | 
			
		||||
	if (!rtp_info)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
        sin = (struct sockaddr_in *)dest;
 | 
			
		||||
 | 
			
		||||
        sin->sin_family = AF_INET;
 | 
			
		||||
        sin->sin_addr.s_addr = inet_addr(rtp_info->addr);
 | 
			
		||||
        sin->sin_port = osmo_ntohs(rtp_info->port);
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 *rtp_info;
 | 
			
		||||
 | 
			
		||||
	rtp_info = osmo_mgcpc_ep_ci_get_rtp_info(ci);
 | 
			
		||||
	if (!rtp_info)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (!rtp_info->x_osmo_osmux_use)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	*cid = rtp_info->x_osmo_osmux_cid;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct osmo_tdef_state_timeout osmo_mgcpc_ep_fsm_timeouts[32] = {
 | 
			
		||||
	[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = { .T=2427001 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Transition to a state, using the T timer defined in assignment_fsm_timeouts.
 | 
			
		||||
 * The actual timeout value is in turn obtained from osmo_mgcpc_ep.T_defs.
 | 
			
		||||
 * Assumes local variable fi exists. */
 | 
			
		||||
#define osmo_mgcpc_ep_fsm_state_chg(state) \
 | 
			
		||||
	osmo_tdef_fsm_inst_state_chg(fi, state, osmo_mgcpc_ep_fsm_timeouts, \
 | 
			
		||||
				     ((struct osmo_mgcpc_ep*)fi->priv)->T_defs, 5)
 | 
			
		||||
 | 
			
		||||
/*! Dispatch an actual CRCX/MDCX/DLCX message for this connection.
 | 
			
		||||
 * \param ci  Connection identifier as obtained from osmo_mgcpc_ep_ci_add().
 | 
			
		||||
 * \param verb  MGCP operation to dispatch.
 | 
			
		||||
 * \param verb_info  Parameters for the MGCP operation.
 | 
			
		||||
 * \param notify  Peer FSM instance to notify of completed/failed operation.
 | 
			
		||||
 * \param event_success  Which event to dispatch to 'notify' upon OK response.
 | 
			
		||||
 * \param event_failure  Which event to dispatch to 'notify' upon failure response.
 | 
			
		||||
 * \param notify_data  Data pointer to pass to the event dispatch for both success and failure.
 | 
			
		||||
 */
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_mgcpc_ep *ep;
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	struct osmo_mgcpc_ep_ci cleared_ci;
 | 
			
		||||
	ci = osmo_mgcpc_ep_check_ci(ci);
 | 
			
		||||
 | 
			
		||||
	if (!ci) {
 | 
			
		||||
		LOGP(DLGLOBAL, LOGL_ERROR, "Invalid MGW endpoint request: no ci\n");
 | 
			
		||||
		goto dispatch_error;
 | 
			
		||||
	}
 | 
			
		||||
	if (!verb_info && verb != MGCP_VERB_DLCX) {
 | 
			
		||||
		LOG_CI(ci, LOGL_ERROR, "Invalid MGW endpoint request: missing verb details for %s\n",
 | 
			
		||||
		       osmo_mgcp_verb_name(verb));
 | 
			
		||||
		goto dispatch_error;
 | 
			
		||||
	}
 | 
			
		||||
	if ((verb < 0) || (verb > MGCP_VERB_RSIP)) {
 | 
			
		||||
		LOG_CI(ci, LOGL_ERROR, "Invalid MGW endpoint request: unknown verb: %s\n",
 | 
			
		||||
		       osmo_mgcp_verb_name(verb));
 | 
			
		||||
		goto dispatch_error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ep = ci->ep;
 | 
			
		||||
	fi = ep->fi;
 | 
			
		||||
 | 
			
		||||
	/* Clear volatile state by explicitly keeping those that should remain. Because we can't assign
 | 
			
		||||
	 * the char[] directly, dance through cleared_ci and copy back. */
 | 
			
		||||
	cleared_ci = (struct osmo_mgcpc_ep_ci){
 | 
			
		||||
		.ep = ep,
 | 
			
		||||
		.mgcp_client_fi = ci->mgcp_client_fi,
 | 
			
		||||
		.got_port_info = ci->got_port_info,
 | 
			
		||||
		.rtp_info = ci->rtp_info,
 | 
			
		||||
 | 
			
		||||
		.occupied = true,
 | 
			
		||||
		/* .pending = true follows below */
 | 
			
		||||
		.verb = verb,
 | 
			
		||||
		.notify = notify,
 | 
			
		||||
		.notify_success = event_success,
 | 
			
		||||
		.notify_failure = event_failure,
 | 
			
		||||
		.notify_data = notify_data,
 | 
			
		||||
	};
 | 
			
		||||
	osmo_strlcpy(cleared_ci.label, ci->label, sizeof(cleared_ci.label));
 | 
			
		||||
	osmo_strlcpy(cleared_ci.mgcp_ci_str, ci->mgcp_ci_str, sizeof(cleared_ci.mgcp_ci_str));
 | 
			
		||||
	*ci = cleared_ci;
 | 
			
		||||
 | 
			
		||||
	LOG_CI_VERB(ci, LOGL_DEBUG, "notify=%s\n", osmo_fsm_inst_name(ci->notify));
 | 
			
		||||
 | 
			
		||||
	if (verb_info)
 | 
			
		||||
		ci->verb_info = *verb_info;
 | 
			
		||||
 | 
			
		||||
	if (ep->endpoint[0]) {
 | 
			
		||||
		if (ci->verb_info.endpoint[0] && strcmp(ci->verb_info.endpoint, ep->endpoint))
 | 
			
		||||
			LOG_CI(ci, LOGL_ERROR,
 | 
			
		||||
			       "Warning: Requested %s on endpoint %s, but this CI is on endpoint %s."
 | 
			
		||||
			       " Using the proper endpoint instead.\n",
 | 
			
		||||
			       osmo_mgcp_verb_name(verb), ci->verb_info.endpoint, ep->endpoint);
 | 
			
		||||
		osmo_strlcpy(ci->verb_info.endpoint, ep->endpoint, sizeof(ci->verb_info.endpoint));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (ci->verb) {
 | 
			
		||||
	case MGCP_VERB_CRCX:
 | 
			
		||||
		if (ci->mgcp_client_fi) {
 | 
			
		||||
			LOG_CI(ci, LOGL_ERROR, "CRCX can be called only once per MGW endpoint CI\n");
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MGCP_VERB_MDCX:
 | 
			
		||||
		if (!ci->mgcp_client_fi) {
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_ERROR, "The first verb on an unused MGW endpoint CI must be CRCX, not %s\n",
 | 
			
		||||
				    osmo_mgcp_verb_name(ci->verb));
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MGCP_VERB_DLCX:
 | 
			
		||||
		if (!ci->mgcp_client_fi) {
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_DEBUG, "Ignoring DLCX on unused MGW endpoint CI\n");
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		LOG_CI(ci, LOGL_ERROR, "This verb is not supported: %s\n", osmo_mgcp_verb_name(ci->verb));
 | 
			
		||||
		on_failure(ci);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ci->pending = true;
 | 
			
		||||
 | 
			
		||||
	LOG_CI_VERB(ci, LOGL_DEBUG, "Scheduling\n");
 | 
			
		||||
 | 
			
		||||
	if (ep->fi->state != OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
 | 
			
		||||
		osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE);
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
dispatch_error:
 | 
			
		||||
	if (notify)
 | 
			
		||||
		osmo_fsm_inst_dispatch(notify, event_failure, notify_data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int send_verb(struct osmo_mgcpc_ep_ci *ci)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = ci->ep;
 | 
			
		||||
 | 
			
		||||
	if (!ci->occupied || !ci->pending || ci->sent)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	switch (ci->verb) {
 | 
			
		||||
 | 
			
		||||
	case MGCP_VERB_CRCX:
 | 
			
		||||
		OSMO_ASSERT(!ci->mgcp_client_fi);
 | 
			
		||||
		LOG_CI_VERB(ci, LOGL_DEBUG, "Sending\n");
 | 
			
		||||
		ci->mgcp_client_fi = mgcp_conn_create(ep->mgcp_client, ep->fi,
 | 
			
		||||
						      CI_EV_FAILURE(ci), CI_EV_SUCCESS(ci),
 | 
			
		||||
						      &ci->verb_info);
 | 
			
		||||
		ci->sent = true;
 | 
			
		||||
		if (!ci->mgcp_client_fi){
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_ERROR, "Cannot send\n");
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		osmo_fsm_inst_update_id(ci->mgcp_client_fi, ci->label);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MGCP_VERB_MDCX:
 | 
			
		||||
		OSMO_ASSERT(ci->mgcp_client_fi);
 | 
			
		||||
		LOG_CI_VERB(ci, LOGL_DEBUG, "Sending\n");
 | 
			
		||||
		rc = mgcp_conn_modify(ci->mgcp_client_fi, CI_EV_SUCCESS(ci), &ci->verb_info);
 | 
			
		||||
		ci->sent = true;
 | 
			
		||||
		if (rc) {
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_ERROR, "Cannot send (rc=%d %s)\n", rc, strerror(-rc));
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MGCP_VERB_DLCX:
 | 
			
		||||
		LOG_CI(ci, LOGL_DEBUG, "Sending MGCP: %s %s\n",
 | 
			
		||||
		       osmo_mgcp_verb_name(ci->verb), ci->mgcp_ci_str);
 | 
			
		||||
		/* The way this is designed, we actually need to forget all about the ci right away. */
 | 
			
		||||
		mgcp_conn_delete(ci->mgcp_client_fi);
 | 
			
		||||
		if (ci->notify)
 | 
			
		||||
			osmo_fsm_inst_dispatch(ci->notify, ci->notify_success, ci->notify_data);
 | 
			
		||||
		*ci = (struct osmo_mgcpc_ep_ci){
 | 
			
		||||
			.ep = ep,
 | 
			
		||||
		};
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		OSMO_ASSERT(false);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! DLCX all connections, terminate the endpoint FSM and free. */
 | 
			
		||||
void osmo_mgcpc_ep_clear(struct osmo_mgcpc_ep *ep)
 | 
			
		||||
{
 | 
			
		||||
	if (!ep)
 | 
			
		||||
		return;
 | 
			
		||||
	osmo_fsm_inst_term(ep->fi, OSMO_FSM_TERM_REGULAR, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void osmo_mgcpc_ep_count(struct osmo_mgcpc_ep *ep, int *occupied, int *pending_not_sent,
 | 
			
		||||
				int *waiting_for_response)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	if (occupied)
 | 
			
		||||
		*occupied = 0;
 | 
			
		||||
 | 
			
		||||
	if (pending_not_sent)
 | 
			
		||||
		*pending_not_sent = 0;
 | 
			
		||||
 | 
			
		||||
	if (waiting_for_response)
 | 
			
		||||
		*waiting_for_response = 0;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
 | 
			
		||||
		struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
 | 
			
		||||
		if (ci->occupied) {
 | 
			
		||||
			if (occupied)
 | 
			
		||||
				(*occupied)++;
 | 
			
		||||
		} else
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (ci->pending)
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_DEBUG, "%s\n",
 | 
			
		||||
				    ci->sent ? "waiting for response" : "waiting to be sent");
 | 
			
		||||
		else
 | 
			
		||||
			LOG_CI_VERB(ci, LOGL_DEBUG, "done (%s)\n", mgcp_conn_peer_name(osmo_mgcpc_ep_ci_get_rtp_info(ci)));
 | 
			
		||||
 | 
			
		||||
		if (ci->pending && ci->sent)
 | 
			
		||||
			if (waiting_for_response)
 | 
			
		||||
				(*waiting_for_response)++;
 | 
			
		||||
		if (ci->pending && !ci->sent)
 | 
			
		||||
			if (pending_not_sent)
 | 
			
		||||
				(*pending_not_sent)++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool osmo_mgcpc_ep_fsm_check_state_chg_after_response(struct osmo_fsm_inst *fi)
 | 
			
		||||
{
 | 
			
		||||
	int waiting_for_response;
 | 
			
		||||
	int occupied;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
 | 
			
		||||
	osmo_mgcpc_ep_count(ep, &occupied, NULL, &waiting_for_response);
 | 
			
		||||
	LOG_MGCPC_EP(ep, LOGL_DEBUG, "CI in use: %d, waiting for response: %d\n", occupied, waiting_for_response);
 | 
			
		||||
 | 
			
		||||
	if (!occupied)  {
 | 
			
		||||
		/* All CI have been released. The endpoint no longer exists. Notify the parent FSM, by
 | 
			
		||||
		 * terminating. */
 | 
			
		||||
		osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, 0);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!waiting_for_response) {
 | 
			
		||||
		if (fi->state != OSMO_MGCPC_EP_ST_IN_USE)
 | 
			
		||||
			osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_IN_USE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void osmo_mgcpc_ep_fsm_wait_mgw_response_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	static int re_enter = 0;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int count = 0;
 | 
			
		||||
	int i;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
 | 
			
		||||
	re_enter++;
 | 
			
		||||
	OSMO_ASSERT(re_enter < 10);
 | 
			
		||||
 | 
			
		||||
	/* The first CRCX gives us the endpoint name in the CRCX response. So we must wait for the first CRCX endpoint
 | 
			
		||||
	 * response to come in before sending any other MGCP requests -- otherwise we might end up creating new
 | 
			
		||||
	 * endpoints instead of acting on the same. This FSM always sends out N requests and waits for all of them to
 | 
			
		||||
	 * complete before sending out new requests. Hence we're safe when the very first time at most one request is
 | 
			
		||||
	 * sent (which needs to be a CRCX). */
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
 | 
			
		||||
		struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
 | 
			
		||||
 | 
			
		||||
		/* Make sure that only CRCX get dispatched if no CRCX were sent yet. */
 | 
			
		||||
		if (!ep->first_crcx_complete) {
 | 
			
		||||
			if (ci->occupied && ci->verb != MGCP_VERB_CRCX)
 | 
			
		||||
				continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc = send_verb(&ep->ci[i]);
 | 
			
		||||
		/* Need to be careful not to access the instance after failure. Event chains may already have
 | 
			
		||||
		 * deallocated this memory. */
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return;
 | 
			
		||||
		if (!rc)
 | 
			
		||||
			continue;
 | 
			
		||||
		count++;
 | 
			
		||||
		/* Make sure that we wait for the first CRCX response before dispatching more requests. */
 | 
			
		||||
		if (!ep->first_crcx_complete)
 | 
			
		||||
			break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOG_MGCPC_EP(ep, LOGL_DEBUG, "Sent messages: %d\n", count);
 | 
			
		||||
	if (ep->first_crcx_complete)
 | 
			
		||||
		osmo_mgcpc_ep_fsm_check_state_chg_after_response(fi);
 | 
			
		||||
	re_enter--;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void osmo_mgcpc_ep_fsm_handle_ci_events(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_mgcpc_ep_ci *ci;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
	ci = osmo_mgcpc_ep_ci_for_event(ep, event);
 | 
			
		||||
	if (ci) {
 | 
			
		||||
		if (event == CI_EV_SUCCESS(ci))
 | 
			
		||||
			on_success(ci, data);
 | 
			
		||||
		else
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void osmo_mgcpc_ep_fsm_in_use_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 | 
			
		||||
{
 | 
			
		||||
	int pending_not_sent;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
 | 
			
		||||
	osmo_mgcpc_ep_count(ep, NULL, &pending_not_sent, NULL);
 | 
			
		||||
	if (pending_not_sent)
 | 
			
		||||
		osmo_mgcpc_ep_fsm_state_chg(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define S(x)	(1 << (x))
 | 
			
		||||
 | 
			
		||||
static const struct osmo_fsm_state osmo_mgcpc_ep_fsm_states[] = {
 | 
			
		||||
	[OSMO_MGCPC_EP_ST_UNUSED] = {
 | 
			
		||||
		.name = "UNUSED",
 | 
			
		||||
		.in_event_mask = 0,
 | 
			
		||||
		.out_state_mask = 0
 | 
			
		||||
			| S(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
 | 
			
		||||
			,
 | 
			
		||||
	},
 | 
			
		||||
	[OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE] = {
 | 
			
		||||
		.name = "WAIT_MGW_RESPONSE",
 | 
			
		||||
		.onenter = osmo_mgcpc_ep_fsm_wait_mgw_response_onenter,
 | 
			
		||||
		.action = osmo_mgcpc_ep_fsm_handle_ci_events,
 | 
			
		||||
		.in_event_mask = 0xffffffff,
 | 
			
		||||
		.out_state_mask = 0
 | 
			
		||||
			| S(OSMO_MGCPC_EP_ST_IN_USE)
 | 
			
		||||
			,
 | 
			
		||||
	},
 | 
			
		||||
	[OSMO_MGCPC_EP_ST_IN_USE] = {
 | 
			
		||||
		.name = "IN_USE",
 | 
			
		||||
		.onenter = osmo_mgcpc_ep_fsm_in_use_onenter,
 | 
			
		||||
		.action = osmo_mgcpc_ep_fsm_handle_ci_events,
 | 
			
		||||
		.in_event_mask = 0xffffffff, /* mgcp_client_fsm may send parent term anytime */
 | 
			
		||||
		.out_state_mask = 0
 | 
			
		||||
			| S(OSMO_MGCPC_EP_ST_WAIT_MGW_RESPONSE)
 | 
			
		||||
			,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int osmo_mgcpc_ep_fsm_timer_cb(struct osmo_fsm_inst *fi)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct osmo_mgcpc_ep *ep = osmo_mgcpc_ep_fi_mgwep(fi);
 | 
			
		||||
 | 
			
		||||
	switch (fi->T) {
 | 
			
		||||
	default:
 | 
			
		||||
		for (i = 0; i < ARRAY_SIZE(ep->ci); i++) {
 | 
			
		||||
			struct osmo_mgcpc_ep_ci *ci = &ep->ci[i];
 | 
			
		||||
			if (!ci->occupied)
 | 
			
		||||
				continue;
 | 
			
		||||
			if (!(ci->pending && ci->sent))
 | 
			
		||||
				continue;
 | 
			
		||||
			on_failure(ci);
 | 
			
		||||
		}
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct osmo_fsm osmo_mgcpc_ep_fsm = {
 | 
			
		||||
	.name = "mgw-endp",
 | 
			
		||||
	.states = osmo_mgcpc_ep_fsm_states,
 | 
			
		||||
	.num_states = ARRAY_SIZE(osmo_mgcpc_ep_fsm_states),
 | 
			
		||||
	.log_subsys = DLMGCP,
 | 
			
		||||
	.event_names = osmo_mgcpc_ep_fsm_event_names,
 | 
			
		||||
	.timer_cb = osmo_mgcpc_ep_fsm_timer_cb,
 | 
			
		||||
	/* The FSM termination will automatically trigger any mgcp_client_fsm instances to DLCX. */
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										745
									
								
								src/libosmo-mgcp-client/mgcp_client_fsm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										745
									
								
								src/libosmo-mgcp-client/mgcp_client_fsm.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,745 @@
 | 
			
		||||
/* (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>
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 (strlen(mgcp_ctx->conn_id)) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_ERROR,
 | 
			
		||||
			 "MGW/DLCX: abrupt FSM termination with connections still present, sending unconditional DLCX...\n");
 | 
			
		||||
		msg = make_dlcx_msg(mgcp_ctx);
 | 
			
		||||
		OSMO_ASSERT(msg);
 | 
			
		||||
		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,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! 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;
 | 
			
		||||
	static bool fsm_registered = false;
 | 
			
		||||
	struct osmo_fsm_inst *fi;
 | 
			
		||||
	struct in_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_aton(conn_peer->addr, &ip_test) == 0)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* Register the fsm description (if not already done) */
 | 
			
		||||
	if (fsm_registered == false) {
 | 
			
		||||
		osmo_fsm_register(&fsm_mgcp_client);
 | 
			
		||||
		fsm_registered = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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 in_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_aton(conn_peer->addr, &ip_test) == 0) {
 | 
			
		||||
		LOGPFSML(fi, LOGL_ERROR, "Cannot MDCX, IP address == 0.0.0.0\n");
 | 
			
		||||
		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);
 | 
			
		||||
 | 
			
		||||
	/* Unlink FSM from parent */
 | 
			
		||||
	osmo_fsm_inst_unlink_parent(fi, NULL);
 | 
			
		||||
 | 
			
		||||
	/* 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;
 | 
			
		||||
}
 | 
			
		||||
@@ -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>
 | 
			
		||||
 * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c)
 | 
			
		||||
 * (C) 2009 by Harald Welte <laforge@gnumonks.org>
 | 
			
		||||
@@ -6,16 +6,16 @@
 | 
			
		||||
 * 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
 | 
			
		||||
 * 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 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/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
@@ -25,32 +25,37 @@
 | 
			
		||||
#include <talloc.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/misc.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/legacy_mgcp/vty.h>
 | 
			
		||||
#include <osmocom/mgcp_client/mgcp_client.h>
 | 
			
		||||
 | 
			
		||||
#define MGCPGW_STR "MGCP gateway configuration for RTP streams\n"
 | 
			
		||||
#define MGW_STR MGCP_CLIENT_MGW_STR
 | 
			
		||||
 | 
			
		||||
void *global_mgcp_client_ctx = NULL;
 | 
			
		||||
struct mgcp_client_conf *global_mgcp_client_conf = NULL;
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcpgw_local_ip, cfg_mgcpgw_local_ip_cmd,
 | 
			
		||||
      "mgcpgw local-ip A.B.C.D",
 | 
			
		||||
      MGCPGW_STR "local bind to connect to MGCP gateway with\n"
 | 
			
		||||
DEFUN(cfg_mgw_local_ip, cfg_mgw_local_ip_cmd,
 | 
			
		||||
      "mgw local-ip A.B.C.D",
 | 
			
		||||
      MGW_STR "local bind to connect to MGW from\n"
 | 
			
		||||
      "local bind IP address\n")
 | 
			
		||||
{
 | 
			
		||||
	if (!global_mgcp_client_conf)
 | 
			
		||||
		return CMD_ERR_NOTHING_TODO;
 | 
			
		||||
	OSMO_ASSERT(global_mgcp_client_ctx);
 | 
			
		||||
	global_mgcp_client_conf->local_addr =
 | 
			
		||||
		talloc_strdup(global_mgcp_client_ctx, argv[0]);
 | 
			
		||||
	osmo_talloc_replace_string(global_mgcp_client_ctx,
 | 
			
		||||
				   (char**)&global_mgcp_client_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_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd,
 | 
			
		||||
      "mgcpgw local-port <0-65535>",
 | 
			
		||||
      MGCPGW_STR "local bind to connect to MGCP gateway with\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")
 | 
			
		||||
{
 | 
			
		||||
	if (!global_mgcp_client_conf)
 | 
			
		||||
@@ -58,61 +63,93 @@ DEFUN(cfg_mgcpgw_local_port, cfg_mgcpgw_local_port_cmd,
 | 
			
		||||
	global_mgcp_client_conf->local_port = atoi(argv[0]);
 | 
			
		||||
	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,
 | 
			
		||||
      "mgcpgw remote-ip A.B.C.D",
 | 
			
		||||
      MGCPGW_STR "remote bind to connect to MGCP gateway with\n"
 | 
			
		||||
      "remote bind IP address\n")
 | 
			
		||||
DEFUN(cfg_mgw_remote_ip, cfg_mgw_remote_ip_cmd,
 | 
			
		||||
      "mgw remote-ip A.B.C.D",
 | 
			
		||||
      MGW_STR "remote IP address to reach the MGW at\n"
 | 
			
		||||
      "remote IP address\n")
 | 
			
		||||
{
 | 
			
		||||
	if (!global_mgcp_client_conf)
 | 
			
		||||
		return CMD_ERR_NOTHING_TODO;
 | 
			
		||||
	OSMO_ASSERT(global_mgcp_client_ctx);
 | 
			
		||||
	global_mgcp_client_conf->remote_addr =
 | 
			
		||||
		talloc_strdup(global_mgcp_client_ctx, argv[0]);
 | 
			
		||||
	osmo_talloc_replace_string(global_mgcp_client_ctx,
 | 
			
		||||
				   (char**)&global_mgcp_client_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",
 | 
			
		||||
		 MGW_STR "remote bind to connect to MGCP gateway with\n"
 | 
			
		||||
		 "remote bind IP address\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcpgw_remote_port, cfg_mgcpgw_remote_port_cmd,
 | 
			
		||||
      "mgcpgw remote-port <0-65535>",
 | 
			
		||||
      MGCPGW_STR "remote bind to connect to MGCP gateway with\n"
 | 
			
		||||
      "remote bind port\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)
 | 
			
		||||
		return CMD_ERR_NOTHING_TODO;
 | 
			
		||||
	global_mgcp_client_conf->remote_port = atoi(argv[0]);
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
ALIAS_DEPRECATED(cfg_mgw_remote_port, cfg_mgcpgw_remote_port_cmd,
 | 
			
		||||
		 "mgcpgw remote-port <0-65535>",
 | 
			
		||||
		 MGW_STR "remote bind to connect to MGCP gateway with\n"
 | 
			
		||||
		 "remote bind port\n")
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_mgcpgw_endpoint_range, cfg_mgcpgw_endpoint_range_cmd,
 | 
			
		||||
      "mgcpgw endpoint-range <1-65534> <1-65534>",
 | 
			
		||||
      MGCPGW_STR "usable range of endpoint identifiers\n"
 | 
			
		||||
      "set first useable endpoint identifier\n"
 | 
			
		||||
      "set the last useable endpoint identifier\n")
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgw_endpoint_range, cfg_mgw_endpoint_range_cmd,
 | 
			
		||||
      "mgw endpoint-range <1-65534> <1-65534>",
 | 
			
		||||
      MGW_STR "DEPRECATED: the endpoint range cannot be defined by the client\n"
 | 
			
		||||
      "-\n" "-\n")
 | 
			
		||||
{
 | 
			
		||||
	uint16_t first_endpoint = atoi(argv[0]);
 | 
			
		||||
	uint16_t last_endpoint = atoi(argv[1]);
 | 
			
		||||
 | 
			
		||||
	if (last_endpoint < first_endpoint) {
 | 
			
		||||
		vty_out(vty, "last endpoint must be greater than first endpoint!%s",
 | 
			
		||||
			VTY_NEWLINE);
 | 
			
		||||
		return CMD_SUCCESS;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	global_mgcp_client_conf->first_endpoint = first_endpoint;
 | 
			
		||||
	global_mgcp_client_conf->last_endpoint = last_endpoint;
 | 
			
		||||
	vty_out(vty, "Please do not use legacy config 'mgw endpoint-range'"
 | 
			
		||||
		" (the range can no longer be defined by the MGCP client)%s",
 | 
			
		||||
		VTY_NEWLINE);
 | 
			
		||||
	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 UDP_PORT_STR "UDP Port number\n"
 | 
			
		||||
DEFUN(cfg_mgcp_rtp_bts_base_port,
 | 
			
		||||
      cfg_mgcp_rtp_bts_base_port_cmd,
 | 
			
		||||
DEFUN_DEPRECATED(cfg_mgw_rtp_bts_base_port,
 | 
			
		||||
      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_STR
 | 
			
		||||
      MGW_STR
 | 
			
		||||
      BTS_START_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]);
 | 
			
		||||
	if (osmo_strlcpy(global_mgcp_client_conf->endpoint_domain_name, argv[0],
 | 
			
		||||
			 sizeof(global_mgcp_client_conf->endpoint_domain_name))
 | 
			
		||||
	    >= sizeof(global_mgcp_client_conf->endpoint_domain_name)) {
 | 
			
		||||
		vty_out(vty, "%% Error: 'mgw endpoint-domain' name too long, max length is %zu: '%s'%s",
 | 
			
		||||
			sizeof(global_mgcp_client_conf->endpoint_domain_name) - 1, argv[0], VTY_NEWLINE);
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
	}
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -120,40 +157,28 @@ int mgcp_client_config_write(struct vty *vty, const char *indent)
 | 
			
		||||
{
 | 
			
		||||
	const char *addr;
 | 
			
		||||
	int port;
 | 
			
		||||
	uint16_t first_endpoint;
 | 
			
		||||
	uint16_t last_endpoint;
 | 
			
		||||
	uint16_t bts_base;
 | 
			
		||||
 | 
			
		||||
	addr = global_mgcp_client_conf->local_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);
 | 
			
		||||
	port = global_mgcp_client_conf->local_port;
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	addr = global_mgcp_client_conf->remote_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);
 | 
			
		||||
	port = global_mgcp_client_conf->remote_port;
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	first_endpoint = global_mgcp_client_conf->first_endpoint;
 | 
			
		||||
	last_endpoint = global_mgcp_client_conf->last_endpoint;
 | 
			
		||||
	if (last_endpoint != 0) {
 | 
			
		||||
		vty_out(vty, "%smgcpgw endpoint-range %u %u%s", indent,
 | 
			
		||||
			first_endpoint, last_endpoint, VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bts_base = global_mgcp_client_conf->bts_base;
 | 
			
		||||
	if (bts_base) {
 | 
			
		||||
		vty_out(vty, "%smgcpgw bts-base %u%s", indent,
 | 
			
		||||
			bts_base, VTY_NEWLINE);
 | 
			
		||||
	}
 | 
			
		||||
	if (global_mgcp_client_conf->endpoint_domain_name[0])
 | 
			
		||||
		vty_out(vty, "%smgw endpoint-domain %s%s", indent,
 | 
			
		||||
			global_mgcp_client_conf->endpoint_domain_name, VTY_NEWLINE);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
@@ -163,10 +188,21 @@ void mgcp_client_vty_init(void *talloc_ctx, int node, struct mgcp_client_conf *c
 | 
			
		||||
	global_mgcp_client_ctx = talloc_ctx;
 | 
			
		||||
	global_mgcp_client_conf = conf;
 | 
			
		||||
 | 
			
		||||
	install_element(node, &cfg_mgw_local_ip_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_local_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_remote_ip_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_remote_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_endpoint_range_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_rtp_bts_base_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgw_endpoint_domain_name_cmd);
 | 
			
		||||
 | 
			
		||||
	/* deprecated 'mgcpgw' commands */
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_local_ip_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_local_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_remote_ip_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_remote_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_endpoint_range_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgcp_rtp_bts_base_port_cmd);
 | 
			
		||||
	install_element(node, &cfg_mgcpgw_rtp_bts_base_port_cmd);
 | 
			
		||||
 | 
			
		||||
	osmo_fsm_vty_add_cmds();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								src/libosmo-mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/libosmo-mgcp/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_builddir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_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 \
 | 
			
		||||
	iuup_protocol.c \
 | 
			
		||||
	iuup_cn_node.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
							
								
								
									
										211
									
								
								src/libosmo-mgcp/iuup_cn_node.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/libosmo-mgcp/iuup_cn_node.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,211 @@
 | 
			
		||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
/* IuUP Core Network side protocol handling, minimal implementation */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Hofmeyr <neels@hofmeyr.de>
 | 
			
		||||
 *
 | 
			
		||||
 * 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 <talloc.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/netif/rtp.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/iuup_cn_node.h>
 | 
			
		||||
#include <osmocom/mgcp/iuup_protocol.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
 | 
			
		||||
#define LOG_IUUP_CN(cn, level, fmt, args...) \
 | 
			
		||||
		LOGP(DIUUP, level, "(%s) " fmt, (cn)->name, ## args)
 | 
			
		||||
 | 
			
		||||
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
 | 
			
		||||
				       const char *name_fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list ap;
 | 
			
		||||
	struct osmo_iuup_cn *cn = talloc_zero(ctx, struct osmo_iuup_cn);
 | 
			
		||||
	OSMO_ASSERT(cn);
 | 
			
		||||
 | 
			
		||||
	cn->cfg = *cfg;
 | 
			
		||||
 | 
			
		||||
	if (!name_fmt)
 | 
			
		||||
		name_fmt = "-";
 | 
			
		||||
 | 
			
		||||
	va_start(ap, name_fmt);
 | 
			
		||||
	cn->name = talloc_vasprintf(cn, name_fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	LOGP(DIUUP, LOGL_INFO, "(%s) Initializing IuUP node\n", cn->name);
 | 
			
		||||
 | 
			
		||||
	if (!osmo_identifier_valid(cn->name)) {
 | 
			
		||||
		LOGP(DIUUP, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
 | 
			
		||||
		     osmo_quote_str(cn->name, -1));
 | 
			
		||||
		talloc_free(cn);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return cn;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn)
 | 
			
		||||
{
 | 
			
		||||
	talloc_free(cn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
 | 
			
		||||
		   struct osmo_iuup_hdr_data *hdr)
 | 
			
		||||
{
 | 
			
		||||
	/* Remove the IuUP bit from the middle of the buffer by writing the RTP header forward. */
 | 
			
		||||
	/* And append AMR 12.2k header "0xf03c". - AD HOC fix */
 | 
			
		||||
	unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
 | 
			
		||||
	memmove(pdu->data + sizeof(*hdr) - 2, pdu->data, pre_hdr_len);
 | 
			
		||||
	((uint8_t*)hdr)[2] = 0xf0;
 | 
			
		||||
	((uint8_t*)hdr)[3] = 0x3c;
 | 
			
		||||
	msgb_pull(pdu, sizeof(*hdr) - 2);
 | 
			
		||||
 | 
			
		||||
	LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
 | 
			
		||||
	cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
 | 
			
		||||
{
 | 
			
		||||
	/* Send Initialization Ack PDU back to the sender */
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
 | 
			
		||||
	OSMO_ASSERT(ack);
 | 
			
		||||
	ack->dst = src_pdu->dst;
 | 
			
		||||
 | 
			
		||||
	/* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
 | 
			
		||||
	memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
 | 
			
		||||
 | 
			
		||||
	osmo_iuup_make_init_ack(ack);
 | 
			
		||||
 | 
			
		||||
	LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
 | 
			
		||||
	rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
 | 
			
		||||
	msgb_free(ack);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
 | 
			
		||||
		      struct osmo_iuup_hdr_ctrl *hdr)
 | 
			
		||||
{
 | 
			
		||||
	switch (hdr->procedure) {
 | 
			
		||||
	case OSMO_IUUP_PROC_INITIALIZATION:
 | 
			
		||||
		switch (hdr->ack_nack) {
 | 
			
		||||
		case OSMO_IUUP_ACKNACK_PROCEDURE:
 | 
			
		||||
			LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
 | 
			
		||||
			cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
 | 
			
		||||
			return tx_init_ack(cn, pdu);
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
 | 
			
		||||
			     cn->name, hdr->ack_nack);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		/* Continue to log "unexpected procedure" below. */
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case OSMO_IUUP_PROC_ERROR_EVENT:
 | 
			
		||||
		{
 | 
			
		||||
			union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
 | 
			
		||||
			LOGP(DIUUP, LOGL_ERROR,
 | 
			
		||||
			     "(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
 | 
			
		||||
			     cn->name, p->error_event.error_distance, p->error_event.error_cause,
 | 
			
		||||
			     osmo_iuup_error_cause_name(p->error_event.error_cause));
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	LOG_IUUP_CN(cn, LOGL_ERROR,
 | 
			
		||||
		    "Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
 | 
			
		||||
		    hdr->procedure, hdr->ack_nack);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
 | 
			
		||||
 * freed by the caller. */
 | 
			
		||||
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
 | 
			
		||||
{
 | 
			
		||||
	struct osmo_iuup_hdr_ctrl *is_ctrl;
 | 
			
		||||
	struct osmo_iuup_hdr_data *is_data;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
 | 
			
		||||
	if (rc)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	if (is_ctrl)
 | 
			
		||||
		return rx_control(cn, pdu, is_ctrl);
 | 
			
		||||
	if (is_data)
 | 
			
		||||
		return rx_data(cn, pdu, is_data);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t frame_nr = cn->next_frame_nr;
 | 
			
		||||
	cn->next_frame_nr = (frame_nr + 1) & 0x0f;
 | 
			
		||||
	return frame_nr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
 | 
			
		||||
 * message to the IuUP peer.
 | 
			
		||||
 * Returns 0 on success, negative on error. */
 | 
			
		||||
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
 | 
			
		||||
{
 | 
			
		||||
	struct rtp_hdr *rtp_was, *rtp;
 | 
			
		||||
	struct osmo_iuup_hdr_data *iuup_hdr;
 | 
			
		||||
 | 
			
		||||
	/* Splice an IuUP header in between RTP header and payload data */
 | 
			
		||||
	rtp_was = (void*)pdu->data;
 | 
			
		||||
 | 
			
		||||
	/* copy the RTP header part backwards by the size needed for the IuUP header */
 | 
			
		||||
	/* also strips 2 bytes from the front of RTP payload - AMR header - AD HOC fix */
 | 
			
		||||
	rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr) - 2);
 | 
			
		||||
	memmove(rtp, rtp_was, sizeof(*rtp));
 | 
			
		||||
 | 
			
		||||
	/* The IuUP side negotiated a payload type number to use during Initialization. The RTP packet going to the IuUP
 | 
			
		||||
	 * peer should reflect this payload_type. This should already have happened in mgcp_patch_pt(), but can't hurt
 | 
			
		||||
	 * to patch over it again. */
 | 
			
		||||
	rtp->payload_type = cn->rtp_payload_type;
 | 
			
		||||
 | 
			
		||||
	iuup_hdr = (void*)rtp->data;
 | 
			
		||||
 | 
			
		||||
	*iuup_hdr = (struct osmo_iuup_hdr_data){
 | 
			
		||||
		.pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
 | 
			
		||||
		.frame_nr = next_frame_nr(cn),
 | 
			
		||||
		.frame_good = OSMO_IUUP_FRAME_GOOD,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
 | 
			
		||||
	LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
 | 
			
		||||
	     cn->name, iuup_hdr->frame_nr);
 | 
			
		||||
 | 
			
		||||
	return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										286
									
								
								src/libosmo-mgcp/iuup_protocol.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								src/libosmo-mgcp/iuup_protocol.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,286 @@
 | 
			
		||||
/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
 | 
			
		||||
/* IuUP Core Network side protocol, minimal implementation */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Neels Hofmeyr <neels@hofmeyr.de>
 | 
			
		||||
 *
 | 
			
		||||
 * 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/mgcp/iuup_protocol.h>
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
#include <osmocom/netif/rtp.h>
 | 
			
		||||
 | 
			
		||||
/* Calculating two bytes of CRC is ok to do by a loop */
 | 
			
		||||
static uint8_t header_crc6(const uint8_t *hdr)
 | 
			
		||||
{
 | 
			
		||||
    int bit;
 | 
			
		||||
    /* Polynomial: D^6 + D^5 + D^3 + D^2 + D^1 + 1
 | 
			
		||||
     * that's 1101111 or 0x6f;
 | 
			
		||||
     * align its lowest bit with a uint16_t's highest bit: */
 | 
			
		||||
    uint32_t polynomial = 0x6f << 15; // 00110111 10000000 00000000
 | 
			
		||||
    uint32_t remainder = ( ((uint32_t)hdr[0]) << 8 | hdr[1] ) << 6;
 | 
			
		||||
 | 
			
		||||
    for (bit = 15; bit >= 0; bit--)
 | 
			
		||||
    {
 | 
			
		||||
        if (remainder & (0x40 << bit))
 | 
			
		||||
            remainder ^= polynomial;
 | 
			
		||||
        polynomial >>= 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return remainder;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Charles Michael Heard's CRC-10 code, from
 | 
			
		||||
 *
 | 
			
		||||
 *      http://web.archive.org/web/20061005231950/http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/crc10.html
 | 
			
		||||
 *
 | 
			
		||||
 * with the CRC table initialized with values computed by
 | 
			
		||||
 * his "gen_byte_crc10_table()" routine, rather than by calling that
 | 
			
		||||
 * routine at run time, and with various data type cleanups.
 | 
			
		||||
 */
 | 
			
		||||
static const uint16_t byte_crc10_table[256] = {
 | 
			
		||||
	0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
 | 
			
		||||
	0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
 | 
			
		||||
	0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
 | 
			
		||||
	0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
 | 
			
		||||
	0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
 | 
			
		||||
	0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
 | 
			
		||||
	0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
 | 
			
		||||
	0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
 | 
			
		||||
	0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
 | 
			
		||||
	0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
 | 
			
		||||
	0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
 | 
			
		||||
	0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
 | 
			
		||||
	0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
 | 
			
		||||
	0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
 | 
			
		||||
	0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
 | 
			
		||||
	0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
 | 
			
		||||
	0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
 | 
			
		||||
	0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
 | 
			
		||||
	0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
 | 
			
		||||
	0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
 | 
			
		||||
	0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
 | 
			
		||||
	0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
 | 
			
		||||
	0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
 | 
			
		||||
	0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
 | 
			
		||||
	0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
 | 
			
		||||
	0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
 | 
			
		||||
	0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
 | 
			
		||||
	0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
 | 
			
		||||
	0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
 | 
			
		||||
	0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
 | 
			
		||||
	0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
 | 
			
		||||
	0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static uint16_t crc10(uint16_t crc10_accum, const uint8_t *payload, unsigned int payload_len)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < payload_len; i++) {
 | 
			
		||||
		crc10_accum = ((crc10_accum << 8) & 0x300)
 | 
			
		||||
			^ byte_crc10_table[(crc10_accum >> 2) & 0xff]
 | 
			
		||||
			^ payload[i];
 | 
			
		||||
	}
 | 
			
		||||
	return crc10_accum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* When a payload of a multiple of bytes has run through, we need to still feed 10 bits of zeros into the
 | 
			
		||||
 * CRC10 to get the payload's checksum result that we can send to a peer. That can't be done with above
 | 
			
		||||
 * table, because it acts as if full 16 bits are fed. This stops after 10 bits. */
 | 
			
		||||
static uint16_t crc10_remainder(uint16_t crc10_accum)
 | 
			
		||||
{
 | 
			
		||||
    int bit;
 | 
			
		||||
    /* Polynomial: D^10 + D^9 + D^5 + D^4 + D^1 + 1
 | 
			
		||||
     * that's 11000110011 or 0x633;
 | 
			
		||||
     * align its lowest bit with a 10bit value's highest bit: */
 | 
			
		||||
    uint32_t polynomial = 0x633 << 9; // 1100 01100110 00000000
 | 
			
		||||
    uint32_t remainder = ((uint32_t)crc10_accum) << 10;
 | 
			
		||||
 | 
			
		||||
    /* Run on 10 bits */
 | 
			
		||||
    for (bit = 9; bit >= 0; bit--)
 | 
			
		||||
    {
 | 
			
		||||
        if (remainder & ((1 << 10) << bit))
 | 
			
		||||
            remainder ^= polynomial;
 | 
			
		||||
        polynomial >>= 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return remainder & 0x3ff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static uint16_t payload_crc10(const uint8_t *payload, unsigned int payload_len)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t crc10_accum = crc10(0, payload, payload_len);
 | 
			
		||||
	return crc10_remainder(crc10_accum);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Given an IuUP PDU data block, write the correct header and payload CRC checksums at the right places.
 | 
			
		||||
 */
 | 
			
		||||
void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len)
 | 
			
		||||
{
 | 
			
		||||
	/* For both data and ctrl, the checksums and payload are at the same offset */
 | 
			
		||||
	struct osmo_iuup_hdr_data *hdr = (void*)iuup_header_and_payload;
 | 
			
		||||
	uint16_t crc;
 | 
			
		||||
	unsigned int payload_len;
 | 
			
		||||
 | 
			
		||||
	hdr->header_crc = header_crc6(iuup_header_and_payload);
 | 
			
		||||
 | 
			
		||||
	payload_len = iuup_header_and_payload + header_and_payload_len - hdr->payload;
 | 
			
		||||
	crc = payload_crc10(hdr->payload, payload_len);
 | 
			
		||||
	hdr->payload_crc_hi = (crc >> 8) & 0x3;
 | 
			
		||||
	hdr->payload_crc_lo = crc & 0xff;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Validate minimum message sizes, IuUP PDU type, header- and payload checksums. If it is a Control
 | 
			
		||||
 * Procedure PDU, return the header position in is_ctrl, if it is a Data PDU, return the header position
 | 
			
		||||
 * in is_data. If log_errors is true, log on DIUUP with the given log label for context. Return NULL in
 | 
			
		||||
 * both is_ctrl and is_data, and return a negative error code if the PDU could not be identified as a
 | 
			
		||||
 * valid RTP PDU containing an IuUP part. */
 | 
			
		||||
int osmo_iuup_classify(bool log_errors,
 | 
			
		||||
		       const char *log_label,
 | 
			
		||||
		       struct msgb *pdu,
 | 
			
		||||
		       struct osmo_iuup_hdr_ctrl **is_ctrl,
 | 
			
		||||
		       struct osmo_iuup_hdr_data **is_data)
 | 
			
		||||
{
 | 
			
		||||
	struct rtp_hdr *rtp = (void*)pdu->data;
 | 
			
		||||
	struct osmo_iuup_hdr_ctrl *hdr = (void*)rtp->data;
 | 
			
		||||
	unsigned int payload_len;
 | 
			
		||||
	uint16_t crc_calculated;
 | 
			
		||||
	uint16_t crc_from_peer;
 | 
			
		||||
 | 
			
		||||
#define ERR(fmt, args...) do { \
 | 
			
		||||
			if (log_errors) \
 | 
			
		||||
				LOGP(DIUUP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
 | 
			
		||||
			return -EINVAL; \
 | 
			
		||||
		} while (0)
 | 
			
		||||
 | 
			
		||||
	if (is_ctrl)
 | 
			
		||||
		*is_ctrl = NULL;
 | 
			
		||||
	if (is_data)
 | 
			
		||||
		*is_data = NULL;
 | 
			
		||||
 | 
			
		||||
	/* We need at least a header of 4 bytes. The osmo_iuup_hdr_ctrl already includes a byte of
 | 
			
		||||
	 * payload, so use osmo_iuup_hdr_data to check the minimum here. */
 | 
			
		||||
	if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_data)))
 | 
			
		||||
		ERR("IuUP PDU too short: %u\n", pdu->len);
 | 
			
		||||
 | 
			
		||||
	/* Let's not validate checksums if the header type isn't sane */
 | 
			
		||||
	switch (hdr->pdu_type) {
 | 
			
		||||
	case OSMO_IUUP_PDU_DATA_WITH_CRC:
 | 
			
		||||
		/* If the caller isn't interested in data PDUs, cut short here. */
 | 
			
		||||
		if (!is_data)
 | 
			
		||||
			return 0;
 | 
			
		||||
		break;
 | 
			
		||||
	case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
 | 
			
		||||
		/* If the caller isn't interested in control PDUs, cut short here. */
 | 
			
		||||
		if (!is_ctrl)
 | 
			
		||||
			return 0;
 | 
			
		||||
		if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_ctrl)))
 | 
			
		||||
			ERR("IuUP control PDU too short: %u\n", pdu->len);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* For both data and ctrl, the checksums and payload are at the same offset */
 | 
			
		||||
 | 
			
		||||
	crc_calculated = header_crc6((uint8_t*)hdr);
 | 
			
		||||
	if (crc_calculated != hdr->header_crc)
 | 
			
		||||
		ERR("IuUP PDU with invalid header CRC (peer sent 0x%x, calculated 0x%x)\n",
 | 
			
		||||
		    hdr->header_crc, crc_calculated);
 | 
			
		||||
 | 
			
		||||
	payload_len = pdu->tail - hdr->payload;
 | 
			
		||||
	crc_calculated = payload_crc10(hdr->payload, payload_len);
 | 
			
		||||
	crc_from_peer = (((uint16_t)hdr->payload_crc_hi) << 8) | hdr->payload_crc_lo;
 | 
			
		||||
	if (crc_from_peer != crc_calculated)
 | 
			
		||||
		ERR("IuUP PDU with invalid payload CRC (peer sent 0x%x, calculated 0x%x)\n",
 | 
			
		||||
		    crc_from_peer, crc_calculated);
 | 
			
		||||
 | 
			
		||||
	switch (hdr->pdu_type) {
 | 
			
		||||
	case OSMO_IUUP_PDU_DATA_WITH_CRC:
 | 
			
		||||
		if (is_data)
 | 
			
		||||
			*is_data = (void*)hdr;
 | 
			
		||||
		return 0;
 | 
			
		||||
	case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
 | 
			
		||||
		if (is_ctrl)
 | 
			
		||||
			*is_ctrl = hdr;
 | 
			
		||||
		return 0;
 | 
			
		||||
	default:
 | 
			
		||||
		ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
 | 
			
		||||
	}
 | 
			
		||||
#undef ERR
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return true if this RTP packet contains an IuUP Initialization header (detect IuUP peer). */
 | 
			
		||||
bool osmo_iuup_is_init(struct msgb *pdu)
 | 
			
		||||
{
 | 
			
		||||
 	struct osmo_iuup_hdr_ctrl *is_ctrl;
 | 
			
		||||
	osmo_iuup_classify(false, NULL, pdu, &is_ctrl, NULL);
 | 
			
		||||
	return is_ctrl
 | 
			
		||||
		&& is_ctrl->procedure == OSMO_IUUP_PROC_INITIALIZATION
 | 
			
		||||
		&& is_ctrl->ack_nack == OSMO_IUUP_ACKNACK_PROCEDURE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Append an IuUP Initialization ACK message */
 | 
			
		||||
void osmo_iuup_make_init_ack(struct msgb *ack)
 | 
			
		||||
{
 | 
			
		||||
	/* Send Initialization Ack PDU back to the sender */
 | 
			
		||||
 	struct osmo_iuup_hdr_ctrl *hdr;
 | 
			
		||||
	OSMO_ASSERT(ack);
 | 
			
		||||
 | 
			
		||||
	hdr = (void*)msgb_put(ack, sizeof(*hdr));
 | 
			
		||||
 | 
			
		||||
	*hdr = (struct osmo_iuup_hdr_ctrl){
 | 
			
		||||
		.pdu_type = OSMO_IUUP_PDU_CONTROL_PROCEDURE,
 | 
			
		||||
		.ack_nack = OSMO_IUUP_ACKNACK_ACK,
 | 
			
		||||
		.procedure = OSMO_IUUP_PROC_INITIALIZATION,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	osmo_iuup_set_checksums((uint8_t*)hdr, sizeof(*hdr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct value_string osmo_iuup_error_cause_names[] = {
 | 
			
		||||
	{ 0, "CRC error of frame header" },
 | 
			
		||||
	{ 1, "CRC error of frame payload" },
 | 
			
		||||
	{ 2, "Unexpected frame number" },
 | 
			
		||||
	{ 3, "Frame loss" },
 | 
			
		||||
	{ 4, "PDU type unknown" },
 | 
			
		||||
	{ 5, "Unknown procedure" },
 | 
			
		||||
	{ 6, "Unknown reserved value" },
 | 
			
		||||
	{ 7, "Unknown field" },
 | 
			
		||||
	{ 8, "Frame too short" },
 | 
			
		||||
	{ 9, "Missing fields" },
 | 
			
		||||
	{ 16, "Unexpected PDU type" },
 | 
			
		||||
	{ 17, "spare" },
 | 
			
		||||
	{ 18, "Unexpected procedure" },
 | 
			
		||||
	{ 19, "Unexpected RFCI" },
 | 
			
		||||
	{ 20, "Unexpected value" },
 | 
			
		||||
	{ 42, "Initialisation failure" },
 | 
			
		||||
	{ 43, "Initialisation failure (network error, timer expiry)" },
 | 
			
		||||
	{ 44, "Initialisation failure (Iu UP function error, repeated NACK)" },
 | 
			
		||||
	{ 45, "Rate control failure" },
 | 
			
		||||
	{ 46, "Error event failure" },
 | 
			
		||||
	{ 47, "Time Alignment not supported" },
 | 
			
		||||
	{ 48, "Requested Time Alignment not possible" },
 | 
			
		||||
	{ 49, "Iu UP Mode version not supported" },
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										469
									
								
								src/libosmo-mgcp/mgcp_codec.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										469
									
								
								src/libosmo-mgcp/mgcp_codec.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,469 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (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_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.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,
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void codec_free(struct mgcp_rtp_codec *codec)
 | 
			
		||||
{
 | 
			
		||||
	if (codec->subtype_name)
 | 
			
		||||
		talloc_free(codec->subtype_name);
 | 
			
		||||
	if (codec->audio_name)
 | 
			
		||||
		talloc_free(codec->audio_name);
 | 
			
		||||
	*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;
 | 
			
		||||
	char audio_codec[64];
 | 
			
		||||
	struct mgcp_rtp_codec *codec;
 | 
			
		||||
	unsigned int pt_offset = conn->end.codecs_assigned;
 | 
			
		||||
	void *ctx = conn->conn;
 | 
			
		||||
 | 
			
		||||
	/* 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:
 | 
			
		||||
			audio_name = talloc_strdup(ctx, "PCMU/8000/1");
 | 
			
		||||
			break;
 | 
			
		||||
		case 3:
 | 
			
		||||
			audio_name = talloc_strdup(ctx, "GSM/8000/1");
 | 
			
		||||
			break;
 | 
			
		||||
		case 8:
 | 
			
		||||
			audio_name = talloc_strdup(ctx, "PCMA/8000/1");
 | 
			
		||||
			break;
 | 
			
		||||
		case 18:
 | 
			
		||||
			audio_name = talloc_strdup(ctx, "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;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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(audio_name) > sizeof(audio_codec)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Audio codec too long: %s\n", osmo_quote_str(audio_name, -1));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
	channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
 | 
			
		||||
	rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
 | 
			
		||||
	if (sscanf(audio_name, "%63[^/]/%d/%d", audio_codec, &rate, &channels) < 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Invalid audio codec: %s\n", osmo_quote_str(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(audio_name, -1));
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	codec->rate = rate;
 | 
			
		||||
	codec->channels = channels;
 | 
			
		||||
	codec->subtype_name = talloc_strdup(ctx, audio_codec);
 | 
			
		||||
	codec->audio_name = talloc_strdup(ctx, audio_name);
 | 
			
		||||
	codec->payload_type = payload_type;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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)
 | 
			
		||||
{
 | 
			
		||||
	char codec_name[64];
 | 
			
		||||
 | 
			
		||||
	/* 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 (!codec->subtype_name)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* We now extract the codec_name (letters before the /, e.g. "GSM"
 | 
			
		||||
	 * from the audio name that is stored in the trunk configuration.
 | 
			
		||||
	 * We do not compare to the full audio_name because we expect that
 | 
			
		||||
	 * "GSM", "GSM/8000" and "GSM/8000/1" are all compatible when the
 | 
			
		||||
	 * audio name of the codec is set to "GSM" */
 | 
			
		||||
	if (sscanf(endp->tcfg->audio_name, "%63[^/]/%*d/%*d", codec_name) < 1)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* Finally we check if the subtype_name we have generated from the
 | 
			
		||||
	 * audio_name in the trunc struct patches the codec_name of the
 | 
			
		||||
	 * given codec */
 | 
			
		||||
	if (strcasecmp(codec_name, codec->subtype_name) == 0)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: It is questinable that the method to pick a compatible
 | 
			
		||||
	 * codec can work properly. Since this useses tcfg->audio_name, as
 | 
			
		||||
	 * a reference, which is set to "AMR/8000" permanently.
 | 
			
		||||
	 * tcfg->audio_name must be updated by the first connection that
 | 
			
		||||
	 * has been made on an endpoint, so that the second connection
 | 
			
		||||
	 * can make a meaningful decision here */
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! 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->tcfg->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
 | 
			
		||||
 */
 | 
			
		||||
static bool 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;
 | 
			
		||||
	if (!strcmp(codec_a->subtype_name, "AMR")) {
 | 
			
		||||
		if (amr_is_octet_aligned(codec_a) != amr_is_octet_aligned(codec_b))
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										397
									
								
								src/libosmo-mgcp/mgcp_conn.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										397
									
								
								src/libosmo-mgcp/mgcp_conn.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,397 @@
 | 
			
		||||
/* 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 <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.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 unsigned int 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 = &conn_rtp->rate_ctr_group->ctr[IN_STREAM_ERR_TSTMP_CTR];
 | 
			
		||||
	conn_rtp->state.out_stream.err_ts_ctr = &conn_rtp->rate_ctr_group->ctr[OUT_STREAM_ERR_TSTMP_CTR];
 | 
			
		||||
	rate_ctr_index++;
 | 
			
		||||
 | 
			
		||||
	/* 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->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);
 | 
			
		||||
	llist_add(&conn->entry, &endp->conns);
 | 
			
		||||
 | 
			
		||||
	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_trunk_config *trunk, struct mgcp_conn_rtp *conn_rtp)
 | 
			
		||||
{
 | 
			
		||||
	struct rate_ctr_group *all_stats = trunk->all_rtp_conn_stats;
 | 
			
		||||
	struct rate_ctr_group *conn_stats = conn_rtp->rate_ctr_group;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < conn_stats->desc->num_ctr; i++)
 | 
			
		||||
		rate_ctr_add(&all_stats->ctr[i], conn_stats->ctr[i].current);
 | 
			
		||||
 | 
			
		||||
	rate_ctr_inc(&all_stats->ctr[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;
 | 
			
		||||
 | 
			
		||||
	/* Run endpoint cleanup action. By this we inform the endpoint about
 | 
			
		||||
	 * the removal of the connection and allow it to clean up its inner
 | 
			
		||||
	 * state accordingly */
 | 
			
		||||
	if (endp->type->cleanup_cb)
 | 
			
		||||
		endp->type->cleanup_cb(endp, conn);
 | 
			
		||||
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		aggregate_rtp_conn_stats(endp->tcfg, &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);
 | 
			
		||||
	llist_del(&conn->entry);
 | 
			
		||||
	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 */
 | 
			
		||||
void mgcp_conn_free_all(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn *conn;
 | 
			
		||||
	struct mgcp_conn *conn_tmp;
 | 
			
		||||
 | 
			
		||||
	/* Drop all items in the list */
 | 
			
		||||
	llist_for_each_entry_safe(conn, conn_tmp, &endp->conns, 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];
 | 
			
		||||
 | 
			
		||||
	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,
 | 
			
		||||
			 inet_ntoa(conn->u.rtp.end.addr),
 | 
			
		||||
			 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										55
									
								
								src/libosmo-mgcp/mgcp_endp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/libosmo-mgcp/mgcp_endp.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
/* 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/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
 | 
			
		||||
/* Endpoint typeset definition */
 | 
			
		||||
const struct mgcp_endpoint_typeset ep_typeset = {
 | 
			
		||||
	/* Specify endpoint properties for RTP endpoint */
 | 
			
		||||
	.rtp.max_conns = 2,
 | 
			
		||||
	.rtp.dispatch_rtp_cb = mgcp_dispatch_rtp_bridge_cb,
 | 
			
		||||
	.rtp.cleanup_cb = mgcp_cleanup_rtp_bridge_cb
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! release endpoint, all open connections are closed.
 | 
			
		||||
 *  \param[in] endp endpoint to release */
 | 
			
		||||
void mgcp_endp_release(struct mgcp_endpoint *endp)
 | 
			
		||||
{
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "Releasing endpoint\n");
 | 
			
		||||
 | 
			
		||||
	/* Normally this function should only be called when
 | 
			
		||||
	 * all connections have been removed already. In case
 | 
			
		||||
	 * that there are still connections open (e.g. when
 | 
			
		||||
	 * RSIP is executed), free them all at once. */
 | 
			
		||||
	mgcp_conn_free_all(endp);
 | 
			
		||||
 | 
			
		||||
	/* Reset endpoint parameters and states */
 | 
			
		||||
	talloc_free(endp->callid);
 | 
			
		||||
	endp->callid = NULL;
 | 
			
		||||
	talloc_free(endp->local_options.string);
 | 
			
		||||
	endp->local_options.string = NULL;
 | 
			
		||||
	talloc_free(endp->local_options.codec);
 | 
			
		||||
	endp->local_options.codec = NULL;
 | 
			
		||||
	endp->wildcarded_req = false;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										514
									
								
								src/libosmo-mgcp/mgcp_msg.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										514
									
								
								src/libosmo-mgcp/mgcp_msg.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,514 @@
 | 
			
		||||
/* 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/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_common.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_msg.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
 | 
			
		||||
/*! Display an mgcp message on the log output.
 | 
			
		||||
 *  \param[in] message mgcp message string
 | 
			
		||||
 *  \param[in] len message mgcp message string length
 | 
			
		||||
 *  \param[in] preamble string to display in logtext in front of each line */
 | 
			
		||||
void mgcp_disp_msg(unsigned char *message, unsigned int len, char *preamble)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char line[80];
 | 
			
		||||
	unsigned char *ptr;
 | 
			
		||||
	unsigned int consumed = 0;
 | 
			
		||||
	unsigned int consumed_line = 0;
 | 
			
		||||
	unsigned int line_count = 0;
 | 
			
		||||
 | 
			
		||||
	if (!log_check_level(DLMGCP, LOGL_DEBUG))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		memset(line, 0, sizeof(line));
 | 
			
		||||
		ptr = line;
 | 
			
		||||
		consumed_line = 0;
 | 
			
		||||
		do {
 | 
			
		||||
			if (*message != '\n' && *message != '\r') {
 | 
			
		||||
				*ptr = *message;
 | 
			
		||||
				ptr++;
 | 
			
		||||
			}
 | 
			
		||||
			message++;
 | 
			
		||||
			consumed++;
 | 
			
		||||
			consumed_line++;
 | 
			
		||||
		} while (*message != '\n' && consumed < len
 | 
			
		||||
			 && consumed_line < sizeof(line));
 | 
			
		||||
 | 
			
		||||
		if (strlen((const char *)line)) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_DEBUG, "%s: line #%02u: %s\n",
 | 
			
		||||
			     preamble, line_count, line);
 | 
			
		||||
			line_count++;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (consumed >= len)
 | 
			
		||||
			return;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Parse connection mode.
 | 
			
		||||
 *  \param[in] mode as string (recvonly, sendrecv, sendonly or loopback)
 | 
			
		||||
 *  \param[in] endp pointer to endpoint (only used for log output)
 | 
			
		||||
 *  \param[out] associated connection to be modified accordingly
 | 
			
		||||
 *  \returns 0 on success, -1 on error */
 | 
			
		||||
int mgcp_parse_conn_mode(const char *mode, struct mgcp_endpoint *endp,
 | 
			
		||||
			 struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	if (!mode) {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "missing connection mode\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return -1;
 | 
			
		||||
	if (!endp)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (strcasecmp(mode, "recvonly") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_RECV_ONLY;
 | 
			
		||||
	else if (strcasecmp(mode, "sendrecv") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_RECV_SEND;
 | 
			
		||||
	else if (strcasecmp(mode, "sendonly") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_SEND_ONLY;
 | 
			
		||||
	else if (strcasecmp(mode, "loopback") == 0)
 | 
			
		||||
		conn->mode = MGCP_CONN_LOOPBACK;
 | 
			
		||||
	else {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "unknown connection mode: '%s'\n", mode);
 | 
			
		||||
		ret = -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Special handling für RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		conn->u.rtp.end.output_enabled =
 | 
			
		||||
		    conn->mode & MGCP_CONN_SEND_ONLY ? 1 : 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_DEBUG, "conn:%s\n", mgcp_conn_dump(conn));
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "connection mode '%s' %d\n",
 | 
			
		||||
		 mode, conn->mode);
 | 
			
		||||
 | 
			
		||||
	/* Special handling für RTP connections */
 | 
			
		||||
	if (conn->type == MGCP_CONN_TYPE_RTP) {
 | 
			
		||||
		LOGPCONN(conn, DLMGCP, LOGL_DEBUG, "output_enabled %d\n",
 | 
			
		||||
			 conn->u.rtp.end.output_enabled);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* The VTY might change the connection mode at any time, so we have
 | 
			
		||||
	 * to hold a copy of the original connection mode */
 | 
			
		||||
	conn->mode_orig = conn->mode;
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* We have a null terminated string with the endpoint name here. We only
 | 
			
		||||
 * support two kinds. Simple ones as seen on the BSC level and the ones
 | 
			
		||||
 * seen on the trunk side. (helper function for find_endpoint()) */
 | 
			
		||||
static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg,
 | 
			
		||||
					      const char *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	char *rest = NULL;
 | 
			
		||||
	struct mgcp_trunk_config *tcfg;
 | 
			
		||||
	int trunk, endp;
 | 
			
		||||
	struct mgcp_endpoint *endp_ptr;
 | 
			
		||||
 | 
			
		||||
	trunk = strtoul(mgcp + 6, &rest, 10);
 | 
			
		||||
	if (rest == NULL || rest[0] != '/' || trunk < 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endp = strtoul(rest + 1, &rest, 10);
 | 
			
		||||
	if (rest == NULL || rest[0] != '@') {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* signalling is on timeslot 1 */
 | 
			
		||||
	if (endp == 1)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	tcfg = mgcp_trunk_num(cfg, trunk);
 | 
			
		||||
	if (!tcfg) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "The trunk %d is not declared.\n",
 | 
			
		||||
		     trunk);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!tcfg->endpoints) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Endpoints of trunk %d not allocated.\n", trunk);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (endp < 1 || endp >= tcfg->number_endpoints) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n",
 | 
			
		||||
		     mgcp);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endp_ptr = &tcfg->endpoints[endp];
 | 
			
		||||
	endp_ptr->wildcarded_req = false;
 | 
			
		||||
	return endp_ptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Find an endpoint that is not in use. Do this by going through the endpoint
 | 
			
		||||
 * array, check the callid. A callid nullpointer indicates that the endpoint
 | 
			
		||||
 * is free */
 | 
			
		||||
static struct mgcp_endpoint *find_free_endpoint(struct mgcp_endpoint *endpoints,
 | 
			
		||||
						unsigned int number_endpoints)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < number_endpoints; i++) {
 | 
			
		||||
		if (endpoints[i].callid == NULL) {
 | 
			
		||||
			endp = &endpoints[i];
 | 
			
		||||
			LOGPENDP(endp, DLMGCP, LOGL_DEBUG,
 | 
			
		||||
			     "found free endpoint\n");
 | 
			
		||||
			endp->wildcarded_req = true;
 | 
			
		||||
			return endp;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Not able to find a free endpoint\n");
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Check if the domain name, which is supplied with the endpoint name
 | 
			
		||||
 * matches the configuration. */
 | 
			
		||||
static int check_domain_name(struct mgcp_config *cfg, const char *mgcp)
 | 
			
		||||
{
 | 
			
		||||
	char *domain_to_check;
 | 
			
		||||
 | 
			
		||||
	domain_to_check = strstr(mgcp, "@");
 | 
			
		||||
	if (!domain_to_check)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Accept any domain if configured as "*" */
 | 
			
		||||
	if (!strcmp(cfg->domain, "*"))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(domain_to_check+1, cfg->domain) != 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Wrong domain name '%s', expecting '%s'\n", mgcp, cfg->domain);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Search the endpoint pool for the endpoint that had been selected via the
 | 
			
		||||
 * MGCP message (helper function for mgcp_analyze_header()) */
 | 
			
		||||
static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg,
 | 
			
		||||
					   const char *mgcp,
 | 
			
		||||
					   int *cause)
 | 
			
		||||
{
 | 
			
		||||
	char *endptr = NULL;
 | 
			
		||||
	unsigned int gw = INT_MAX;
 | 
			
		||||
	const char *endpoint_number_str;
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
 | 
			
		||||
	*cause = 0;
 | 
			
		||||
 | 
			
		||||
	/* Check if the domainname in the request is correct */
 | 
			
		||||
	if (check_domain_name(cfg, mgcp)) {
 | 
			
		||||
		*cause = -500;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if the E1 trunk is requested */
 | 
			
		||||
	if (strncmp(mgcp, "ds/e1", 5) == 0) {
 | 
			
		||||
		endp = find_e1_endpoint(cfg, mgcp);
 | 
			
		||||
		if (!endp)
 | 
			
		||||
			*cause = -500;
 | 
			
		||||
		return endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if the virtual trunk is addressed (new, correct way with prefix) */
 | 
			
		||||
	if (strncmp
 | 
			
		||||
	    (mgcp, MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK,
 | 
			
		||||
	     strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK)) == 0) {
 | 
			
		||||
		endpoint_number_str =
 | 
			
		||||
		    mgcp + strlen(MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK);
 | 
			
		||||
		if (endpoint_number_str[0] == '*') {
 | 
			
		||||
			endp = find_free_endpoint(cfg->trunk.endpoints,
 | 
			
		||||
						  cfg->trunk.number_endpoints);
 | 
			
		||||
			if (!endp)
 | 
			
		||||
				*cause = -403;
 | 
			
		||||
			return endp;
 | 
			
		||||
		}
 | 
			
		||||
		gw = strtoul(endpoint_number_str, &endptr, 16);
 | 
			
		||||
		if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
 | 
			
		||||
			endp = &cfg->trunk.endpoints[gw];
 | 
			
		||||
			endp->wildcarded_req = false;
 | 
			
		||||
			return endp;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Deprecated method without prefix */
 | 
			
		||||
	LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	     "Addressing virtual trunk without prefix (deprecated), please use %s: '%s'\n",
 | 
			
		||||
	     MGCP_ENDPOINT_PREFIX_VIRTUAL_TRUNK, mgcp);
 | 
			
		||||
	gw = strtoul(mgcp, &endptr, 16);
 | 
			
		||||
	if (gw < cfg->trunk.number_endpoints && endptr[0] == '@') {
 | 
			
		||||
		endp = &cfg->trunk.endpoints[gw];
 | 
			
		||||
		endp->wildcarded_req = false;
 | 
			
		||||
		return endp;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp);
 | 
			
		||||
	*cause = -500;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Analyze and parse the the hader of an MGCP messeage string.
 | 
			
		||||
 *  \param[out] pdata caller provided memory to store the parsing results
 | 
			
		||||
 *  \param[in] data mgcp message string
 | 
			
		||||
 *  \returns when the status line was complete and transaction_id and
 | 
			
		||||
 *  endp out parameters are set, -1 on error */
 | 
			
		||||
int mgcp_parse_header(struct mgcp_parse_data *pdata, char *data)
 | 
			
		||||
{
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	char *elem, *save = NULL;
 | 
			
		||||
	int cause;
 | 
			
		||||
 | 
			
		||||
	/*! This function will parse the header part of the received
 | 
			
		||||
	 *  MGCP message. The parsing results are stored in pdata.
 | 
			
		||||
	 *  The function will also automatically search the pool with
 | 
			
		||||
	 *  available endpoints in order to find an endpoint that matches
 | 
			
		||||
	 *  the endpoint string in in the header */
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(data);
 | 
			
		||||
	pdata->trans = "000000";
 | 
			
		||||
 | 
			
		||||
	for (elem = strtok_r(data, " ", &save); elem;
 | 
			
		||||
	     elem = strtok_r(NULL, " ", &save)) {
 | 
			
		||||
		switch (i) {
 | 
			
		||||
		case 0:
 | 
			
		||||
			pdata->trans = elem;
 | 
			
		||||
			break;
 | 
			
		||||
		case 1:
 | 
			
		||||
			pdata->endp = find_endpoint(pdata->cfg, elem, &cause);
 | 
			
		||||
			if (!pdata->endp) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				     "Unable to find Endpoint `%s'\n", elem);
 | 
			
		||||
				OSMO_ASSERT(cause < 0);
 | 
			
		||||
				return cause;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 2:
 | 
			
		||||
			if (strcasecmp("MGCP", elem)) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
				     "MGCP header parsing error\n");
 | 
			
		||||
				return -510;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		case 3:
 | 
			
		||||
			if (strcmp("1.0", elem)) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR, "MGCP version `%s' "
 | 
			
		||||
				     "not supported\n", elem);
 | 
			
		||||
				return -528;
 | 
			
		||||
			}
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		i++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (i != 4) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "MGCP status line too short.\n");
 | 
			
		||||
		pdata->trans = "000000";
 | 
			
		||||
		pdata->endp = NULL;
 | 
			
		||||
		return -510;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Extract OSMUX CID from an MGCP parameter line (string).
 | 
			
		||||
 *  \param[in] line single parameter line from the MGCP message
 | 
			
		||||
 *  \returns OSMUX CID, -1 wildcard, -2 on error */
 | 
			
		||||
int mgcp_parse_osmux_cid(const char *line)
 | 
			
		||||
{
 | 
			
		||||
	int osmux_cid;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	if (strcasecmp(line + 2, "Osmux: *") == 0) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_DEBUG, "Parsed wilcard Osmux CID\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (sscanf(line + 2 + 7, "%u", &osmux_cid) != 1) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Failed parsing Osmux in MGCP msg line: %s\n",
 | 
			
		||||
		     line);
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (osmux_cid > OSMUX_CID_MAX) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n",
 | 
			
		||||
		     osmux_cid, OSMUX_CID_MAX);
 | 
			
		||||
		return -2;
 | 
			
		||||
	}
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid);
 | 
			
		||||
 | 
			
		||||
	return osmux_cid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Check MGCP parameter line (string) for plausibility.
 | 
			
		||||
 *  \param[in] endp pointer to endpoint (only used for log output)
 | 
			
		||||
 *  \param[in] line single parameter line from the MGCP message
 | 
			
		||||
 *  \returns 1 when line seems plausible, 0 on error */
 | 
			
		||||
int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line)
 | 
			
		||||
{
 | 
			
		||||
	const size_t line_len = strlen(line);
 | 
			
		||||
	if (line[0] != '\0' && line_len < 2) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Wrong MGCP option format: '%s' on 0x%x\n",
 | 
			
		||||
		     line, ENDPOINT_NUMBER(endp));
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* FIXME: A couple more checks wouldn't hurt... */
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Check if the specified callid seems plausible.
 | 
			
		||||
  * \param[in] endp pointer to endpoint
 | 
			
		||||
  * \param{in] callid to verify
 | 
			
		||||
  * \returns 1 when callid seems plausible, 0 on error */
 | 
			
		||||
int mgcp_verify_call_id(struct mgcp_endpoint *endp, const char *callid)
 | 
			
		||||
{
 | 
			
		||||
	/*! This function compares the supplied callid with the called that is
 | 
			
		||||
	 *  stored in the endpoint structure. */
 | 
			
		||||
 | 
			
		||||
	if (!endp)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	/* Accept any CallID for "X-Osmo-IGN: C" */
 | 
			
		||||
	if (endp->x_osmo_ign & MGCP_X_OSMO_IGN_CALLID)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (!callid)
 | 
			
		||||
		return -1;
 | 
			
		||||
	if (!endp->callid)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(endp->callid, callid) != 0) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "CallIDs mismatch: '%s' != '%s'\n",
 | 
			
		||||
			 endp->callid, callid);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Check if the specified connection id seems plausible.
 | 
			
		||||
  * \param[in] endp pointer to endpoint
 | 
			
		||||
  * \param{in] connection id to verify
 | 
			
		||||
  * \returns 0 when connection id is valid and exists, an RFC3435 error code on error.
 | 
			
		||||
  */
 | 
			
		||||
int mgcp_verify_ci(struct mgcp_endpoint *endp, const char *conn_id)
 | 
			
		||||
{
 | 
			
		||||
	/* For invalid conn_ids, return 510 "The transaction could not be executed, because some
 | 
			
		||||
	 * unspecified protocol error was detected." */
 | 
			
		||||
 | 
			
		||||
	/* Check for null identifiers */
 | 
			
		||||
	if (!conn_id) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "invalid ConnectionIdentifier (missing)\n");
 | 
			
		||||
		return 510;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check for empty connection identifiers */
 | 
			
		||||
	if (strlen(conn_id) == 0) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "invalid ConnectionIdentifier (empty)\n");
 | 
			
		||||
		return 510;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check for over long connection identifiers */
 | 
			
		||||
	if (strlen(conn_id) > (MGCP_CONN_ID_MAXLEN-1)) {
 | 
			
		||||
		LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			"invalid ConnectionIdentifier (too long: %zu > %d) 0x%s\n",
 | 
			
		||||
			 strlen(conn_id), MGCP_CONN_ID_MAXLEN-1, conn_id);
 | 
			
		||||
		return 510;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check if connection exists */
 | 
			
		||||
	if (mgcp_conn_get(endp, conn_id))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	LOGPENDP(endp, DLMGCP, LOGL_ERROR,
 | 
			
		||||
	     "no connection found under ConnectionIdentifier 0x%s\n", conn_id);
 | 
			
		||||
 | 
			
		||||
	/* When the conn_id was not found, return error code 515 "The transaction refers to an incorrect
 | 
			
		||||
	 * connection-id (may have been already deleted)." */
 | 
			
		||||
	return 515;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Extract individual lines from MCGP message.
 | 
			
		||||
  * \param[in] str MGCP message string, consisting of multiple lines
 | 
			
		||||
  * \param{in] saveptr pointer to next line in str
 | 
			
		||||
  * \returns line, NULL when done */
 | 
			
		||||
char *mgcp_strline(char *str, char **saveptr)
 | 
			
		||||
{
 | 
			
		||||
	char *result;
 | 
			
		||||
 | 
			
		||||
	/*! The function must be called with *str set to the input string
 | 
			
		||||
	 *  for the first line. After that saveptr will be initalized.
 | 
			
		||||
	 *  all consecutive lines are extracted by calling the function
 | 
			
		||||
	 *  with str set to NULL. When done, the function will return NULL
 | 
			
		||||
	 *  to indicate that all lines have been parsed. */
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1600
									
								
								src/libosmo-mgcp/mgcp_network.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1600
									
								
								src/libosmo-mgcp/mgcp_network.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										649
									
								
								src/libosmo-mgcp/mgcp_osmux.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										649
									
								
								src/libosmo-mgcp/mgcp_osmux.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,649 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (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/socket.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/netif/osmux.h>
 | 
			
		||||
#include <osmocom/netif/rtp.h>
 | 
			
		||||
#include <osmocom/netif/amr.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/osmux.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_conn.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.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; /* network byte order */
 | 
			
		||||
	int refcnt;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void *osmux;
 | 
			
		||||
 | 
			
		||||
/* Deliver OSMUX batch to the remote end */
 | 
			
		||||
static void osmux_deliver_cb(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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Lookup existing OSMUX handle for specified destination address. */
 | 
			
		||||
static struct osmux_handle *
 | 
			
		||||
osmux_handle_find_get(struct in_addr *addr, int rem_port)
 | 
			
		||||
{
 | 
			
		||||
	struct osmux_handle *h;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Put down no longer needed OSMUX handle */
 | 
			
		||||
static void osmux_handle_put(struct osmux_in_handle *in)
 | 
			
		||||
{
 | 
			
		||||
	struct osmux_handle *h;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Allocate free OSMUX handle */
 | 
			
		||||
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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* sequence number to start OSMUX message from */
 | 
			
		||||
	h->in->osmux_seq = 0;
 | 
			
		||||
 | 
			
		||||
	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_cb;
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Lookup existing handle for a specified address, if the handle can not be
 | 
			
		||||
 * found, the function will automatically allocate one */
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! send RTP packet through OSMUX connection.
 | 
			
		||||
 *  \param[in] buf rtp data
 | 
			
		||||
 *  \param[in] buf_len length of rtp data
 | 
			
		||||
 *  \param[in] conn associated RTP connection
 | 
			
		||||
 *  \returns 0 on success, -1 on ERROR */
 | 
			
		||||
int osmux_xfrm_to_osmux(char *buf, int buf_len, struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
 | 
			
		||||
	msg = msgb_alloc(4096, "RTP");
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	memcpy(msg->data, buf, buf_len);
 | 
			
		||||
	msgb_put(msg, buf_len);
 | 
			
		||||
 | 
			
		||||
	if (conn->osmux.state != OSMUX_STATE_ENABLED) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_INFO, "forwarding RTP to Osmux conn not yet enabled, dropping (cid=%d)\n",
 | 
			
		||||
		conn->osmux.cid);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while ((ret = osmux_xfrm_input(conn->osmux.in, msg, conn->osmux.cid)) > 0) {
 | 
			
		||||
		/* batch full, build and deliver it */
 | 
			
		||||
		osmux_xfrm_input_deliver(conn->osmux.in);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Lookup the endpoint that corresponds to the specified address (port) */
 | 
			
		||||
static struct mgcp_conn_rtp*
 | 
			
		||||
osmux_conn_lookup(struct mgcp_config *cfg, uint8_t cid,
 | 
			
		||||
		struct in_addr *from_addr)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_endpoint *endp;
 | 
			
		||||
	struct mgcp_conn *conn = NULL;
 | 
			
		||||
	struct mgcp_conn_rtp * conn_rtp;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i=0; i<cfg->trunk.number_endpoints; i++) {
 | 
			
		||||
 | 
			
		||||
		endp = &cfg->trunk.endpoints[i];
 | 
			
		||||
 | 
			
		||||
		llist_for_each_entry(conn, &endp->conns, entry) {
 | 
			
		||||
			if (conn->type != MGCP_CONN_TYPE_RTP)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			conn_rtp = &conn->u.rtp;
 | 
			
		||||
			if (!mgcp_conn_rtp_is_osmux(conn_rtp))
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			if (conn_rtp->osmux.cid == cid)
 | 
			
		||||
				return conn_rtp;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_ERROR, "Cannot find osmux conn with cid=%d\n", cid);
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* FIXME: this is declared and used in mgcp_network.c, but documentation of mgcp_dispatch_rtp_bridge_cb() states another enum is to be used */
 | 
			
		||||
enum {
 | 
			
		||||
	MGCP_PROTO_RTP,
 | 
			
		||||
	MGCP_PROTO_RTCP,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void scheduled_from_osmux_tx_rtp_cb(struct msgb *msg, void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_conn_rtp *conn = data;
 | 
			
		||||
	struct mgcp_endpoint *endp = conn->conn->endp;
 | 
			
		||||
 | 
			
		||||
	endp->type->dispatch_rtp_cb(msg);
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Updates endp osmux state and returns 0 if it can process messages, -1 otherwise */
 | 
			
		||||
static int endp_osmux_state_check(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
				  bool sending)
 | 
			
		||||
{
 | 
			
		||||
	switch(conn->osmux.state) {
 | 
			
		||||
	case OSMUX_STATE_ACTIVATING:
 | 
			
		||||
	if (osmux_enable_conn(endp, conn, &conn->end.addr, conn->end.rtp_port) < 0) {
 | 
			
		||||
			LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
				 "Could not enable osmux for conn on %s: %s\n",
 | 
			
		||||
				 sending ? "sent" : "received",
 | 
			
		||||
				 mgcp_conn_dump(conn->conn));
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "Osmux %s CID %u towards %s:%u is now enabled\n",
 | 
			
		||||
			 sending ? "sent" : "received",
 | 
			
		||||
			 conn->osmux.cid, inet_ntoa(conn->end.addr),
 | 
			
		||||
			 ntohs(conn->end.rtp_port));
 | 
			
		||||
		return 0;
 | 
			
		||||
	case OSMUX_STATE_ENABLED:
 | 
			
		||||
		return 0;
 | 
			
		||||
	default:
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "Osmux %s in conn %s without full negotiation, state %d\n",
 | 
			
		||||
			 sending ? "sent" : "received",
 | 
			
		||||
			 mgcp_conn_dump(conn->conn), conn->osmux.state);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int osmux_legacy_dummy_parse_cid(struct sockaddr_in *addr, struct msgb *msg,
 | 
			
		||||
					uint8_t *osmux_cid)
 | 
			
		||||
{
 | 
			
		||||
	if (msg->len < 1 + sizeof(*osmux_cid)) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Discarding truncated Osmux dummy load: %s\n", osmo_hexdump(msg->data, msg->len));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* extract the osmux CID from the dummy message */
 | 
			
		||||
	memcpy(osmux_cid, &msg->data[1], sizeof(*osmux_cid));
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	uint8_t osmux_cid;
 | 
			
		||||
	struct mgcp_conn_rtp *conn;
 | 
			
		||||
 | 
			
		||||
	if (osmux_legacy_dummy_parse_cid(addr, msg, &osmux_cid) < 0)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	conn = osmux_conn_lookup(cfg, osmux_cid, &addr->sin_addr);
 | 
			
		||||
	if (!conn) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "Cannot find conn for Osmux CID %d\n", osmux_cid);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	endp_osmux_state_check(conn->conn->endp, conn, false);
 | 
			
		||||
	/* Only needed to punch hole in firewall, it can be dropped */
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define osmux_chunk_length(msg, rem) (rem - msg->len);
 | 
			
		||||
 | 
			
		||||
static int osmux_read_fd_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg;
 | 
			
		||||
	struct osmux_hdr *osmuxh;
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	struct mgcp_config *cfg = ofd->data;
 | 
			
		||||
	uint32_t rem;
 | 
			
		||||
	struct mgcp_conn_rtp *conn_src;
 | 
			
		||||
 | 
			
		||||
	msg = osmux_recv(ofd, &addr);
 | 
			
		||||
	if (!msg)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (!cfg->osmux) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
		     "bsc-nat wants to use Osmux but bsc did not request it\n");
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* 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) {
 | 
			
		||||
 | 
			
		||||
		conn_src = osmux_conn_lookup(cfg, osmuxh->circuit_id,
 | 
			
		||||
					     &addr.sin_addr);
 | 
			
		||||
		if (!conn_src) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Cannot find a src conn for circuit_id=%d\n",
 | 
			
		||||
			     osmuxh->circuit_id);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/*conn_dst = mgcp_find_dst_conn(conn_src->conn);
 | 
			
		||||
		if (!conn_dst) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_ERROR,
 | 
			
		||||
			     "Cannot find a dst conn for circuit_id=%d\n",
 | 
			
		||||
			     osmuxh->circuit_id);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}*/
 | 
			
		||||
 | 
			
		||||
		if (endp_osmux_state_check(conn_src->conn->endp, conn_src, false) == 0) {
 | 
			
		||||
			conn_src->osmux.stats.octets += osmux_chunk_length(msg, rem);
 | 
			
		||||
			conn_src->osmux.stats.chunks++;
 | 
			
		||||
			osmux_xfrm_output_sched(&conn_src->osmux.out, osmuxh);
 | 
			
		||||
		}
 | 
			
		||||
		rem = msg->len;
 | 
			
		||||
	}
 | 
			
		||||
out:
 | 
			
		||||
	msgb_free(msg);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int osmux_init(int role, struct mgcp_config *cfg)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	osmux_fd.cb = osmux_read_fd_cb;
 | 
			
		||||
	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 to %s:%u\n",
 | 
			
		||||
		     cfg->osmux_addr, cfg->osmux_port);
 | 
			
		||||
		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 %s\n",
 | 
			
		||||
		     osmo_sock_get_name2(osmux_fd.fd));
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
	cfg->osmux_init = 1;
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_INFO, "OSMUX socket listening on %s\n",
 | 
			
		||||
		 osmo_sock_get_name2(osmux_fd.fd));
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! enable OSXMUX circuit for a specified connection.
 | 
			
		||||
 *  \param[in] endp mgcp endpoint (configuration)
 | 
			
		||||
 *  \param[in] conn connection to disable
 | 
			
		||||
 *  \param[in] addr IP address of remote OSMUX endpoint
 | 
			
		||||
 *  \param[in] port portnumber of the remote OSMUX endpoint (in network byte order)
 | 
			
		||||
 *  \returns 0 on success, -1 on ERROR */
 | 
			
		||||
int osmux_enable_conn(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn,
 | 
			
		||||
		      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 (conn_net->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 OSMUX_CID_MAX + 1 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.
 | 
			
		||||
	 */
 | 
			
		||||
	struct in_addr addr_unset = {};
 | 
			
		||||
	static const uint32_t rtp_ssrc_winlen = UINT32_MAX / (OSMUX_CID_MAX + 1);
 | 
			
		||||
	uint16_t osmux_dummy = endp->cfg->osmux_dummy;
 | 
			
		||||
 | 
			
		||||
	/* Check if osmux is enabled for the specified connection */
 | 
			
		||||
	if (conn->osmux.state != OSMUX_STATE_ACTIVATING) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			 "conn:%s didn't negotiate Osmux, state %d\n",
 | 
			
		||||
			 mgcp_conn_dump(conn->conn), conn->osmux.state);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Wait until we have the connection information from MDCX */
 | 
			
		||||
	if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
 | 
			
		||||
			"Osmux remote address/port still unknown\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	conn->osmux.in = osmux_handle_lookup(endp->cfg, addr, port);
 | 
			
		||||
	if (!conn->osmux.in) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			"Cannot allocate input osmux handle for conn:%s\n",
 | 
			
		||||
			mgcp_conn_dump(conn->conn));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	if (osmux_xfrm_input_open_circuit(conn->osmux.in, conn->osmux.cid, osmux_dummy) < 0) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR,
 | 
			
		||||
			"Cannot open osmux circuit %u for conn:%s\n",
 | 
			
		||||
		     conn->osmux.cid, mgcp_conn_dump(conn->conn));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osmux_xfrm_output_init2(&conn->osmux.out,
 | 
			
		||||
			       (conn->osmux.cid * rtp_ssrc_winlen) +
 | 
			
		||||
			        (random() % rtp_ssrc_winlen),
 | 
			
		||||
			       conn->end.codec->payload_type);
 | 
			
		||||
 | 
			
		||||
	osmux_xfrm_output_set_tx_cb(&conn->osmux.out,
 | 
			
		||||
				    scheduled_from_osmux_tx_rtp_cb, conn);
 | 
			
		||||
 | 
			
		||||
	conn->osmux.state = OSMUX_STATE_ENABLED;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! disable OSXMUX circuit for a specified connection.
 | 
			
		||||
 *  \param[in] conn connection to disable */
 | 
			
		||||
void conn_osmux_disable(struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(conn->osmux.state != OSMUX_STATE_DISABLED);
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
 | 
			
		||||
		"Releasing connection using Osmux CID %u\n", conn->osmux.cid);
 | 
			
		||||
 | 
			
		||||
	if (conn->osmux.state == OSMUX_STATE_ENABLED) {
 | 
			
		||||
		/* We are closing, we don't need pending RTP packets to be transmitted */
 | 
			
		||||
		osmux_xfrm_output_set_tx_cb(&conn->osmux.out, NULL, NULL);
 | 
			
		||||
		osmux_xfrm_output_flush(&conn->osmux.out);
 | 
			
		||||
 | 
			
		||||
		osmux_xfrm_input_close_circuit(conn->osmux.in, conn->osmux.cid);
 | 
			
		||||
		conn->osmux.state = OSMUX_STATE_DISABLED;
 | 
			
		||||
		conn_osmux_release_cid(conn);
 | 
			
		||||
		osmux_handle_put(conn->osmux.in);
 | 
			
		||||
	}
 | 
			
		||||
	conn_osmux_release_cid(conn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! relase OSXMUX cid, that had been allocated to this connection.
 | 
			
		||||
 *  \param[in] conn connection with OSMUX cid to release */
 | 
			
		||||
void conn_osmux_release_cid(struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	if (conn->osmux.cid_allocated)
 | 
			
		||||
		osmux_cid_pool_put(conn->osmux.cid);
 | 
			
		||||
	conn->osmux.cid = 0;
 | 
			
		||||
	conn->osmux.cid_allocated = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! allocate OSXMUX cid to connection.
 | 
			
		||||
 *  \param[in] conn connection for which we allocate the OSMUX cid
 | 
			
		||||
 * \param[in] osmux_cid OSMUX cid to allocate. -1 Means take next available one.
 | 
			
		||||
 * \returns Allocated OSMUX cid, -1 on error (no free  cids avail, or selected one is already taken).
 | 
			
		||||
 */
 | 
			
		||||
int conn_osmux_allocate_cid(struct mgcp_conn_rtp *conn, int osmux_cid)
 | 
			
		||||
{
 | 
			
		||||
	if (osmux_cid != -1 && osmux_cid_pool_allocated((uint8_t) osmux_cid)) {
 | 
			
		||||
		LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
 | 
			
		||||
			 "Osmux CID %d already allocated!\n",
 | 
			
		||||
			 osmux_cid);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (osmux_cid == -1) {
 | 
			
		||||
		osmux_cid = osmux_cid_pool_get_next();
 | 
			
		||||
		if (osmux_cid == -1) {
 | 
			
		||||
			LOGPCONN(conn->conn, DLMGCP, LOGL_INFO,
 | 
			
		||||
				 "no available Osmux CID to allocate!\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	} else
 | 
			
		||||
		osmux_cid_pool_get(osmux_cid);
 | 
			
		||||
 | 
			
		||||
	conn->osmux.cid = (uint8_t) osmux_cid;
 | 
			
		||||
	conn->osmux.cid_allocated = true;
 | 
			
		||||
	conn->type = MGCP_OSMUX_BSC;
 | 
			
		||||
	return osmux_cid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! send RTP dummy packet to OSMUX connection port.
 | 
			
		||||
 *  \param[in] endp mcgp endpoint that holds the RTP connection
 | 
			
		||||
 *  \param[in] conn associated RTP connection
 | 
			
		||||
 *  \returns bytes sent, -1 on error */
 | 
			
		||||
int osmux_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	struct osmux_hdr *osmuxh;
 | 
			
		||||
	int buf_len;
 | 
			
		||||
	struct in_addr addr_unset = {};
 | 
			
		||||
 | 
			
		||||
	/*! The dummy packet will not be sent via the actual OSMUX connection,
 | 
			
		||||
	 *  instead it is sent out of band to port where the remote OSMUX
 | 
			
		||||
	 *  multplexer is listening. The goal is to ensure that the connection
 | 
			
		||||
	 *  is kept open */
 | 
			
		||||
 | 
			
		||||
	/*! 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. */
 | 
			
		||||
 | 
			
		||||
	/* Wait until we have the connection information from MDCX */
 | 
			
		||||
	if (memcmp(&conn->end.addr, &addr_unset, sizeof(addr_unset)) == 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (endp_osmux_state_check(endp, conn, true) < 0)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	buf_len = sizeof(struct osmux_hdr) + osmo_amr_bytes(AMR_FT_0);
 | 
			
		||||
	osmuxh = (struct osmux_hdr *) alloca(buf_len);
 | 
			
		||||
	memset(osmuxh, 0, buf_len);
 | 
			
		||||
	osmuxh->ft = OSMUX_FT_DUMMY;
 | 
			
		||||
	osmuxh->amr_ft = AMR_FT_0;
 | 
			
		||||
	osmuxh->circuit_id = conn->osmux.cid;
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_DEBUG,
 | 
			
		||||
		 "sending OSMUX dummy load to %s:%u CID %u\n",
 | 
			
		||||
		 inet_ntoa(conn->end.addr), ntohs(conn->end.rtp_port), conn->osmux.cid);
 | 
			
		||||
 | 
			
		||||
	return mgcp_udp_send(osmux_fd.fd, &conn->end.addr,
 | 
			
		||||
			     conn->end.rtp_port, (char*)osmuxh, buf_len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* bsc-nat allocates/releases the Osmux circuit ID. +7 to round up to 8 bit boundary. */
 | 
			
		||||
static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1 + 7) / 8];
 | 
			
		||||
 | 
			
		||||
/*! count the number of taken OSMUX cids.
 | 
			
		||||
 *  \returns number of OSMUX cids in use */
 | 
			
		||||
int osmux_cid_pool_count_used(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! take a free OSMUX cid.
 | 
			
		||||
 *  \returns OSMUX cid */
 | 
			
		||||
int osmux_cid_pool_get_next(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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! take a specific OSMUX cid.
 | 
			
		||||
 *  \param[in] osmux_cid OSMUX cid */
 | 
			
		||||
void osmux_cid_pool_get(uint8_t osmux_cid)
 | 
			
		||||
{
 | 
			
		||||
	LOGP(DLMGCP, LOGL_DEBUG, "Allocating Osmux CID %u from pool\n", osmux_cid);
 | 
			
		||||
	osmux_cid_bitmap[osmux_cid / 8] |= (1 << (osmux_cid % 8));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! put back a no longer used OSMUX cid.
 | 
			
		||||
 *  \param[in] osmux_cid OSMUX cid */
 | 
			
		||||
void osmux_cid_pool_put(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));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! check if OSMUX cid is already taken */
 | 
			
		||||
bool osmux_cid_pool_allocated(uint8_t osmux_cid)
 | 
			
		||||
{
 | 
			
		||||
	return !!(osmux_cid_bitmap[osmux_cid / 8] & (1 << (osmux_cid % 8)));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1778
									
								
								src/libosmo-mgcp/mgcp_protocol.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1778
									
								
								src/libosmo-mgcp/mgcp_protocol.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										588
									
								
								src/libosmo-mgcp/mgcp_sdp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										588
									
								
								src/libosmo-mgcp/mgcp_sdp.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,588 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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/core/msgb.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_msg.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_codec.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_sdp.h>
 | 
			
		||||
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
 | 
			
		||||
/* Two structs to store intermediate parsing results. The function
 | 
			
		||||
 * mgcp_parse_sdp_data() is using the following two structs as temporary
 | 
			
		||||
 * storage for parsing the SDP codec information. */
 | 
			
		||||
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;
 | 
			
		||||
};
 | 
			
		||||
struct sdp_fmtp_param {
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	struct mgcp_codec_param param;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Helper function to extrapolate missing codec parameters in a codec mao from
 | 
			
		||||
 * an already filled in payload_type, called from: mgcp_parse_sdp_data() */
 | 
			
		||||
static 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;
 | 
			
		||||
		default:
 | 
			
		||||
			codecs[i].codec_name = NULL;
 | 
			
		||||
			codecs[i].rate = 0;
 | 
			
		||||
			codecs[i].channels = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Helper function to update codec map information with additional data from
 | 
			
		||||
 * SDP, called from: mgcp_parse_sdp_data() */
 | 
			
		||||
static void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used,
 | 
			
		||||
			  int payload, const char *audio_name)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < used; ++i) {
 | 
			
		||||
		char audio_codec[64];
 | 
			
		||||
		int rate = -1;
 | 
			
		||||
		int channels = -1;
 | 
			
		||||
 | 
			
		||||
		/* Note: We can only update payload codecs that already exist
 | 
			
		||||
		 * in our codec list. If we get an unexpected payload type,
 | 
			
		||||
		 * we just drop it */
 | 
			
		||||
		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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Extract payload types from SDP, also check for duplicates */
 | 
			
		||||
static int pt_from_sdp(void *ctx, struct sdp_rtp_map *codecs,
 | 
			
		||||
		       unsigned int codecs_len, char *sdp)
 | 
			
		||||
{
 | 
			
		||||
	char *str;
 | 
			
		||||
	char *str_ptr;
 | 
			
		||||
	char *pt_str;
 | 
			
		||||
	char *pt_end;
 | 
			
		||||
	unsigned long int pt;
 | 
			
		||||
	unsigned int count = 0;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	str = talloc_zero_size(ctx, strlen(sdp) + 1);
 | 
			
		||||
	str_ptr = str;
 | 
			
		||||
	strcpy(str_ptr, sdp);
 | 
			
		||||
 | 
			
		||||
	str_ptr = strstr(str_ptr, "RTP/AVP ");
 | 
			
		||||
	if (!str_ptr)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	pt_str = strtok(str_ptr, " ");
 | 
			
		||||
	if (!pt_str)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		/* Do not allow excessive payload types */
 | 
			
		||||
		if (count > codecs_len)
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		pt_str = strtok(NULL, " ");
 | 
			
		||||
		if (!pt_str)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		errno = 0;
 | 
			
		||||
		pt = strtoul(pt_str, &pt_end, 0);
 | 
			
		||||
		if ((errno == ERANGE && pt == ULONG_MAX) || (errno && !pt) ||
 | 
			
		||||
		    pt_str == pt_end)
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		if (pt >> 7) /* PT is 7 bit field, higher values not allowed */
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		/* Do not allow duplicate payload types */
 | 
			
		||||
		for (i = 0; i < count; i++)
 | 
			
		||||
			if (codecs[i].payload_type == pt)
 | 
			
		||||
				goto error;
 | 
			
		||||
 | 
			
		||||
		codecs[count].payload_type = pt;
 | 
			
		||||
		count++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
	talloc_free(str);
 | 
			
		||||
	return count;
 | 
			
		||||
error:
 | 
			
		||||
	talloc_free(str);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Extract fmtp parameters from SDP, called from: mgcp_parse_sdp_data() */
 | 
			
		||||
static int fmtp_from_sdp(void *ctx, struct sdp_fmtp_param *fmtp_param, char *sdp)
 | 
			
		||||
{
 | 
			
		||||
	char *str;
 | 
			
		||||
	char *str_ptr;
 | 
			
		||||
	char *param_str;
 | 
			
		||||
	unsigned int pt;
 | 
			
		||||
	unsigned int count = 0;
 | 
			
		||||
	char delimiter;
 | 
			
		||||
	unsigned int amr_octet_aligned;
 | 
			
		||||
 | 
			
		||||
	memset(fmtp_param, 0, sizeof(*fmtp_param));
 | 
			
		||||
 | 
			
		||||
	str = talloc_zero_size(ctx, strlen(sdp) + 1);
 | 
			
		||||
	str_ptr = str;
 | 
			
		||||
	strcpy(str_ptr, sdp);
 | 
			
		||||
 | 
			
		||||
	/* Check if the input string begins with an fmtp token */
 | 
			
		||||
	str_ptr = strstr(str_ptr, "fmtp:");
 | 
			
		||||
	if (!str_ptr)
 | 
			
		||||
		goto exit;
 | 
			
		||||
	str_ptr += 5;
 | 
			
		||||
 | 
			
		||||
	/* Extract payload type */
 | 
			
		||||
	if (sscanf(str_ptr, "%u ", &pt) != 1)
 | 
			
		||||
		goto error;
 | 
			
		||||
	fmtp_param->payload_type = pt;
 | 
			
		||||
 | 
			
		||||
	/* Advance pointer to the beginning of the parameter section and
 | 
			
		||||
	 * tokenize string */
 | 
			
		||||
	str_ptr = strstr(str_ptr, " ");
 | 
			
		||||
	if (!str_ptr)
 | 
			
		||||
		goto error;
 | 
			
		||||
	str_ptr++;
 | 
			
		||||
 | 
			
		||||
	param_str = strtok(str_ptr, " ");
 | 
			
		||||
	if (!param_str)
 | 
			
		||||
		goto exit;
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		/* Make sure that we don't get trapped in an endless loop */
 | 
			
		||||
		if (count > 256)
 | 
			
		||||
			goto error;
 | 
			
		||||
 | 
			
		||||
		/* Chop off delimiters ';' at the end */
 | 
			
		||||
		delimiter = str_ptr[strlen(str_ptr) - 1];
 | 
			
		||||
		if (delimiter == ';' || delimiter == ',')
 | 
			
		||||
			str_ptr[strlen(str_ptr) - 1] = '\0';
 | 
			
		||||
 | 
			
		||||
		/* AMR octet aligned parameter */
 | 
			
		||||
		if (sscanf(param_str, "octet-align=%d", &amr_octet_aligned) == 1) {
 | 
			
		||||
			fmtp_param->param.amr_octet_aligned_present = true;
 | 
			
		||||
			fmtp_param->param.amr_octet_aligned = false;
 | 
			
		||||
			if (amr_octet_aligned == 1)
 | 
			
		||||
				fmtp_param->param.amr_octet_aligned = true;
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		param_str = strtok(NULL, " ");
 | 
			
		||||
		if (!param_str)
 | 
			
		||||
			break;
 | 
			
		||||
		count++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
	talloc_free(str);
 | 
			
		||||
	return 0;
 | 
			
		||||
error:
 | 
			
		||||
	talloc_free(str);
 | 
			
		||||
	return -EINVAL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Pick optional fmtp parameters by payload type, if there are no fmtp
 | 
			
		||||
 * parameters, a nullpointer is returned */
 | 
			
		||||
static struct mgcp_codec_param *param_by_pt(int pt, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < fmtp_params_len; i++) {
 | 
			
		||||
		if (fmtp_params[i].payload_type == pt)
 | 
			
		||||
			return &fmtp_params[i].param;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Analyze SDP input string.
 | 
			
		||||
 *  \param[in] endp trunk endpoint.
 | 
			
		||||
 *  \param[out] conn associated rtp connection.
 | 
			
		||||
 *  \param[out] caller provided memory to store the parsing results.
 | 
			
		||||
 *
 | 
			
		||||
 *  Note: In conn (conn->end) the function returns the packet duration,
 | 
			
		||||
 *  rtp port, rtcp port and the codec information.
 | 
			
		||||
 *  \returns 0 on success, -1 on failure. */
 | 
			
		||||
int mgcp_parse_sdp_data(const struct mgcp_endpoint *endp,
 | 
			
		||||
			struct mgcp_conn_rtp *conn, struct mgcp_parse_data *p)
 | 
			
		||||
{
 | 
			
		||||
	struct sdp_rtp_map codecs[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int codecs_used = 0;
 | 
			
		||||
	struct sdp_fmtp_param fmtp_params[MGCP_MAX_CODECS];
 | 
			
		||||
	unsigned int fmtp_used = 0;
 | 
			
		||||
	struct mgcp_codec_param *codec_param;
 | 
			
		||||
	char *line;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	void *tmp_ctx = talloc_new(NULL);
 | 
			
		||||
	struct mgcp_rtp_end *rtp;
 | 
			
		||||
 | 
			
		||||
	int payload;
 | 
			
		||||
	int ptime, ptime2 = 0;
 | 
			
		||||
	char audio_name[64];
 | 
			
		||||
	int port, rc;
 | 
			
		||||
	char ipv4[16];
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
	OSMO_ASSERT(p);
 | 
			
		||||
 | 
			
		||||
	rtp = &conn->end;
 | 
			
		||||
	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':
 | 
			
		||||
			if (sscanf(line, "a=rtpmap:%d %63s", &payload, audio_name) == 2) {
 | 
			
		||||
				codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name);
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			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;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) {
 | 
			
		||||
				rtp->maximum_packet_time = ptime2;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (strncmp("a=fmtp:", line, 6) == 0) {
 | 
			
		||||
				rc = fmtp_from_sdp(conn->conn, &fmtp_params[fmtp_used], line);
 | 
			
		||||
				if (rc >= 0)
 | 
			
		||||
					fmtp_used++;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		case 'm':
 | 
			
		||||
			rc = sscanf(line, "m=audio %d RTP/AVP", &port);
 | 
			
		||||
			if (rc == 1) {
 | 
			
		||||
				rtp->rtp_port = htons(port);
 | 
			
		||||
				rtp->rtcp_port = htons(port + 1);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			rc = pt_from_sdp(conn->conn, codecs,
 | 
			
		||||
					 ARRAY_SIZE(codecs), line);
 | 
			
		||||
			if (rc > 0)
 | 
			
		||||
				codecs_used = rc;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'c':
 | 
			
		||||
 | 
			
		||||
			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;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	OSMO_ASSERT(codecs_used <= MGCP_MAX_CODECS);
 | 
			
		||||
 | 
			
		||||
	/* So far we have only set the payload type in the codec struct. Now we
 | 
			
		||||
	 * fill up the remaining fields of the codec description with some default
 | 
			
		||||
	 * information */
 | 
			
		||||
	codecs_initialize(tmp_ctx, codecs, codecs_used);
 | 
			
		||||
 | 
			
		||||
	/* Store parsed codec information */
 | 
			
		||||
	for (i = 0; i < codecs_used; i++) {
 | 
			
		||||
		codec_param = param_by_pt(codecs[i].payload_type, fmtp_params, fmtp_used);
 | 
			
		||||
		rc = mgcp_codec_add(conn, codecs[i].payload_type, codecs[i].map_line, codec_param);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			LOGP(DLMGCP, LOGL_NOTICE, "endpoint:0x%x, failed to add codec\n", ENDPOINT_NUMBER(p->endp));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talloc_free(tmp_ctx);
 | 
			
		||||
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_NOTICE,
 | 
			
		||||
	     "Got media info via SDP: port:%d, addr:%s, duration:%d, payload-types:",
 | 
			
		||||
	     ntohs(rtp->rtp_port), inet_ntoa(rtp->addr),
 | 
			
		||||
	     rtp->packet_duration_ms);
 | 
			
		||||
	if (codecs_used == 0)
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, "none");
 | 
			
		||||
	for (i = 0; i < codecs_used; i++) {
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, "%d=%s",
 | 
			
		||||
		      rtp->codecs[i].payload_type,
 | 
			
		||||
		      rtp->codecs[i].subtype_name ? rtp-> codecs[i].subtype_name : "unknown");
 | 
			
		||||
		LOGPC(DLMGCP, LOGL_NOTICE, " ");
 | 
			
		||||
	}
 | 
			
		||||
	LOGPC(DLMGCP, LOGL_NOTICE, "\n");
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Add rtpmap string to the sdp payload, but only when the payload type falls
 | 
			
		||||
 * into the dynamic payload type range */
 | 
			
		||||
static int add_rtpmap(struct msgb *sdp, int payload_type, const char *audio_name)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	if (payload_type >= 96 && payload_type <= 127) {
 | 
			
		||||
		if (!audio_name)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		rc = msgb_printf(sdp, "a=rtpmap:%d %s\r\n", payload_type, audio_name);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add audio strings to sdp payload */
 | 
			
		||||
static int add_audio(struct msgb *sdp, int *payload_types, unsigned int payload_types_len, int local_port)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 | 
			
		||||
	rc = msgb_printf(sdp, "m=audio %d RTP/AVP", local_port);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < payload_types_len; i++) {
 | 
			
		||||
		rc = msgb_printf(sdp, " %d", payload_types[i]);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = msgb_printf(sdp, "\r\n");
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Add fmtp strings to sdp payload */
 | 
			
		||||
static int add_fmtp(struct msgb *sdp, struct sdp_fmtp_param *fmtp_params, unsigned int fmtp_params_len,
 | 
			
		||||
		    const char *fmtp_extra)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int fmtp_extra_pt = -1;
 | 
			
		||||
	char *fmtp_extra_pars = "";
 | 
			
		||||
 | 
			
		||||
	/* When no fmtp parameters ara available but an fmtp extra string
 | 
			
		||||
	 * is configured, just add the fmtp extra string */
 | 
			
		||||
	if (fmtp_params_len == 0 && fmtp_extra) {
 | 
			
		||||
		return msgb_printf(sdp, "%s\r\n", fmtp_extra);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* When there is fmtp extra configured we dissect it in order to drop
 | 
			
		||||
	 * in the configured extra parameters at the right place when
 | 
			
		||||
	 * generating the fmtp strings. */
 | 
			
		||||
	if (fmtp_extra) {
 | 
			
		||||
		if (sscanf(fmtp_extra, "a=fmtp:%d ", &fmtp_extra_pt) != 1)
 | 
			
		||||
			fmtp_extra_pt = -1;
 | 
			
		||||
 | 
			
		||||
		fmtp_extra_pars = strstr(fmtp_extra, " ");
 | 
			
		||||
 | 
			
		||||
		if (!fmtp_extra_pars)
 | 
			
		||||
			fmtp_extra_pars = "";
 | 
			
		||||
		else
 | 
			
		||||
			fmtp_extra_pars++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < fmtp_params_len; i++) {
 | 
			
		||||
		rc = msgb_printf(sdp, "a=fmtp:%u", fmtp_params[i].payload_type);
 | 
			
		||||
 | 
			
		||||
		/* Add amr octet align parameter */
 | 
			
		||||
		if (fmtp_params[i].param.amr_octet_aligned_present) {
 | 
			
		||||
			if (fmtp_params[i].param.amr_octet_aligned)
 | 
			
		||||
				rc = msgb_printf(sdp, " octet-align=1");
 | 
			
		||||
			else
 | 
			
		||||
				rc = msgb_printf(sdp, " octet-align=0");
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* Append extra parameters from fmtp extra */
 | 
			
		||||
		if (fmtp_params[i].payload_type == fmtp_extra_pt) {
 | 
			
		||||
			rc = msgb_printf(sdp, " %s", fmtp_extra_pars);
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		rc = msgb_printf(sdp, "\r\n", fmtp_params[i].payload_type);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! Generate SDP response string.
 | 
			
		||||
 *  \param[in] endp trunk endpoint.
 | 
			
		||||
 *  \param[in] conn associated rtp connection.
 | 
			
		||||
 *  \param[out] sdp msg buffer to append resulting SDP string data.
 | 
			
		||||
 *  \param[in] addr IPV4 address string (e.g. 192.168.100.1).
 | 
			
		||||
 *  \returns 0 on success, -1 on failure. */
 | 
			
		||||
int mgcp_write_response_sdp(const struct mgcp_endpoint *endp,
 | 
			
		||||
			    const struct mgcp_conn_rtp *conn, struct msgb *sdp,
 | 
			
		||||
			    const char *addr)
 | 
			
		||||
{
 | 
			
		||||
	const struct mgcp_rtp_codec *codec;
 | 
			
		||||
	const char *fmtp_extra;
 | 
			
		||||
	const char *audio_name;
 | 
			
		||||
	int payload_type;
 | 
			
		||||
	struct sdp_fmtp_param fmtp_param;
 | 
			
		||||
	int rc;
 | 
			
		||||
	int payload_types[1];
 | 
			
		||||
	int local_port;
 | 
			
		||||
	struct sdp_fmtp_param fmtp_params[1];
 | 
			
		||||
        unsigned int fmtp_params_len = 0;
 | 
			
		||||
 | 
			
		||||
	OSMO_ASSERT(endp);
 | 
			
		||||
	OSMO_ASSERT(conn);
 | 
			
		||||
	OSMO_ASSERT(sdp);
 | 
			
		||||
	OSMO_ASSERT(addr);
 | 
			
		||||
 | 
			
		||||
	/* FIXME: constify endp and conn args in get_net_donwlink_format_cb() */
 | 
			
		||||
	endp->cfg->get_net_downlink_format_cb((struct mgcp_endpoint *)endp,
 | 
			
		||||
					      &codec, &fmtp_extra,
 | 
			
		||||
					      (struct mgcp_conn_rtp *)conn);
 | 
			
		||||
 | 
			
		||||
	audio_name = codec->audio_name;
 | 
			
		||||
	payload_type = codec->payload_type;
 | 
			
		||||
 | 
			
		||||
	rc = msgb_printf(sdp,
 | 
			
		||||
			 "v=0\r\n"
 | 
			
		||||
			 "o=- %s 23 IN IP4 %s\r\n"
 | 
			
		||||
			 "s=-\r\n"
 | 
			
		||||
			 "c=IN IP4 %s\r\n"
 | 
			
		||||
			 "t=0 0\r\n", conn->conn->id, addr, addr);
 | 
			
		||||
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		goto buffer_too_small;
 | 
			
		||||
 | 
			
		||||
	if (payload_type >= 0) {
 | 
			
		||||
 | 
			
		||||
		payload_types[0] = payload_type;
 | 
			
		||||
		if (mgcp_conn_rtp_is_osmux(conn))
 | 
			
		||||
			local_port = endp->cfg->osmux_port;
 | 
			
		||||
		else
 | 
			
		||||
			local_port = conn->end.local_port;
 | 
			
		||||
		rc = add_audio(sdp, payload_types, 1, local_port);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto buffer_too_small;
 | 
			
		||||
 | 
			
		||||
		if (endp->tcfg->audio_send_name) {
 | 
			
		||||
			rc = add_rtpmap(sdp, payload_type, audio_name);
 | 
			
		||||
			if (rc < 0)
 | 
			
		||||
				goto buffer_too_small;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (codec->param_present) {
 | 
			
		||||
			fmtp_param.payload_type = payload_type;
 | 
			
		||||
			fmtp_param.param = codec->param;
 | 
			
		||||
			fmtp_params[0] = fmtp_param;
 | 
			
		||||
			fmtp_params_len = 1;
 | 
			
		||||
		}
 | 
			
		||||
		rc = add_fmtp(sdp, fmtp_params, fmtp_params_len, fmtp_extra);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto buffer_too_small;
 | 
			
		||||
	}
 | 
			
		||||
	if (conn->end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) {
 | 
			
		||||
		rc = msgb_printf(sdp, "a=ptime:%u\r\n",
 | 
			
		||||
				 conn->end.packet_duration_ms);
 | 
			
		||||
		if (rc < 0)
 | 
			
		||||
			goto buffer_too_small;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
buffer_too_small:
 | 
			
		||||
	LOGPCONN(conn->conn, DLMGCP, LOGL_ERROR, "SDP messagebuffer too small\n");
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										138
									
								
								src/libosmo-mgcp/mgcp_stat.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/libosmo-mgcp/mgcp_stat.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
/* 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/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/mgcp_stat.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
 | 
			
		||||
/* Helper function for mgcp_format_stats_rtp() to calculate packet loss */
 | 
			
		||||
void calc_loss(struct mgcp_conn_rtp *conn, uint32_t *expected, int *loss)
 | 
			
		||||
{
 | 
			
		||||
	struct mgcp_rtp_state *state = &conn->state;
 | 
			
		||||
	struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
 | 
			
		||||
 | 
			
		||||
	*expected = state->stats.cycles + state->stats.max_seq;
 | 
			
		||||
	*expected = *expected - state->stats.base_seq + 1;
 | 
			
		||||
 | 
			
		||||
	if (!state->stats.initialized) {
 | 
			
		||||
		*expected = 0;
 | 
			
		||||
		*loss = 0;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Make sure the sign is correct and use the biggest
 | 
			
		||||
	 * positive/negative number that fits.
 | 
			
		||||
	 */
 | 
			
		||||
	*loss = *expected - packets_rx->current;
 | 
			
		||||
	if (*expected < packets_rx->current) {
 | 
			
		||||
		if (*loss > 0)
 | 
			
		||||
			*loss = INT_MIN;
 | 
			
		||||
	} else {
 | 
			
		||||
		if (*loss < 0)
 | 
			
		||||
			*loss = INT_MAX;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Helper function for mgcp_format_stats_rtp() to calculate jitter */
 | 
			
		||||
uint32_t calc_jitter(struct mgcp_rtp_state *state)
 | 
			
		||||
{
 | 
			
		||||
	if (!state->stats.initialized)
 | 
			
		||||
		return 0;
 | 
			
		||||
	return state->stats.jitter >> 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Generate statistics for an RTP connection */
 | 
			
		||||
static void mgcp_format_stats_rtp(char *str, size_t str_len,
 | 
			
		||||
				  struct mgcp_conn_rtp *conn)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t expected, jitter;
 | 
			
		||||
	int ploss;
 | 
			
		||||
	int nchars;
 | 
			
		||||
 | 
			
		||||
	struct rate_ctr *packets_rx = &conn->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR];
 | 
			
		||||
	struct rate_ctr *octets_rx = &conn->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR];
 | 
			
		||||
	struct rate_ctr *packets_tx = &conn->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR];
 | 
			
		||||
	struct rate_ctr *octets_tx = &conn->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR];
 | 
			
		||||
 | 
			
		||||
	calc_loss(conn, &expected, &ploss);
 | 
			
		||||
	jitter = calc_jitter(&conn->state);
 | 
			
		||||
 | 
			
		||||
	nchars = snprintf(str, str_len,
 | 
			
		||||
			  "\r\nP: PS=%" PRIu64 ", OS=%" PRIu64 ", PR=%" PRIu64 ", OR=%" PRIu64 ", PL=%d, JI=%u",
 | 
			
		||||
			  packets_tx->current, octets_tx->current,
 | 
			
		||||
			  packets_rx->current, octets_rx->current,
 | 
			
		||||
			  ploss, jitter);
 | 
			
		||||
	if (nchars < 0 || nchars >= str_len)
 | 
			
		||||
		goto truncate;
 | 
			
		||||
 | 
			
		||||
	str += nchars;
 | 
			
		||||
	str_len -= nchars;
 | 
			
		||||
 | 
			
		||||
	if (conn->conn->endp->cfg->osmux != OSMUX_USAGE_OFF) {
 | 
			
		||||
		/* Error Counter */
 | 
			
		||||
		nchars = snprintf(str, str_len,
 | 
			
		||||
				  "\r\nX-Osmo-CP: EC TI=%" PRIu64 ", TO=%" PRIu64,
 | 
			
		||||
				  conn->state.in_stream.err_ts_ctr->current,
 | 
			
		||||
				  conn->state.out_stream.err_ts_ctr->current);
 | 
			
		||||
		if (nchars < 0 || nchars >= str_len)
 | 
			
		||||
			goto truncate;
 | 
			
		||||
 | 
			
		||||
		str += nchars;
 | 
			
		||||
		str_len -= nchars;
 | 
			
		||||
 | 
			
		||||
		if (conn->osmux.state == OSMUX_STATE_ENABLED) {
 | 
			
		||||
			snprintf(str, str_len,
 | 
			
		||||
				 "\r\nX-Osmux-ST: CR=%u, BR=%u",
 | 
			
		||||
				 conn->osmux.stats.chunks, conn->osmux.stats.octets);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
truncate:
 | 
			
		||||
	str[str_len - 1] = '\0';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*! format statistics into an mgcp parameter string.
 | 
			
		||||
 *  \param[out] str resulting string
 | 
			
		||||
 *  \param[in] str_len length of the string buffer
 | 
			
		||||
 *  \param[in] conn connection to evaluate */
 | 
			
		||||
void mgcp_format_stats(char *str, size_t str_len, struct mgcp_conn *conn)
 | 
			
		||||
{
 | 
			
		||||
	memset(str, 0, str_len);
 | 
			
		||||
	if (!conn)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* NOTE: At the moment we only support generating statistics for
 | 
			
		||||
	 * RTP connections. However, in the future we may also want to
 | 
			
		||||
	 * generate statistics for other connection types as well. Lets
 | 
			
		||||
	 * keep this option open: */
 | 
			
		||||
	switch (conn->type) {
 | 
			
		||||
	case MGCP_CONN_TYPE_RTP:
 | 
			
		||||
		mgcp_format_stats_rtp(str, str_len, &conn->u.rtp);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -8,24 +8,23 @@ AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(LIBBCG729_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
bin_PROGRAMS = \
 | 
			
		||||
	osmo-bsc_mgcp \
 | 
			
		||||
	osmo-mgw \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
osmo_bsc_mgcp_SOURCES = \
 | 
			
		||||
	mgcp_main.c \
 | 
			
		||||
osmo_mgw_SOURCES = \
 | 
			
		||||
	mgw_main.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
osmo_bsc_mgcp_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-legacy-mgcp/libosmo-legacy-mgcp.la \
 | 
			
		||||
osmo_mgw_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBBCG729_LIBS) \
 | 
			
		||||
	$(LIBRARY_GSM) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2009-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
 | 
			
		||||
 * (C) 2009-2011 by On-Waves
 | 
			
		||||
 * (C) 2017 by sysmocom - s.f.m.c. GmbH, Author: Philipp Maier
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
@@ -32,9 +33,11 @@
 | 
			
		||||
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/legacy_mgcp/vty.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_internal.h>
 | 
			
		||||
#include <osmocom/mgcp/vty.h>
 | 
			
		||||
#include <osmocom/mgcp/debug.h>
 | 
			
		||||
#include <osmocom/mgcp/mgcp_endp.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
@@ -43,38 +46,37 @@
 | 
			
		||||
#include <osmocom/core/stats.h>
 | 
			
		||||
#include <osmocom/core/rate_ctr.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/socket.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/vty/telnet_interface.h>
 | 
			
		||||
#include <osmocom/vty/logging.h>
 | 
			
		||||
#include <osmocom/vty/ports.h>
 | 
			
		||||
#include <osmocom/vty/command.h>
 | 
			
		||||
#include <osmocom/vty/stats.h>
 | 
			
		||||
#include <osmocom/vty/misc.h>
 | 
			
		||||
 | 
			
		||||
#include "../../bscconfig.h"
 | 
			
		||||
 | 
			
		||||
#ifdef BUILD_MGCP_TRANSCODING
 | 
			
		||||
#include <osmocom/legacy_mgcp/mgcp_transcode.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
 | 
			
		||||
#warning "Make use of the rtp proxy code"
 | 
			
		||||
/* FIXME: Make use of the rtp proxy code */
 | 
			
		||||
 | 
			
		||||
static struct mgcp_config *cfg;
 | 
			
		||||
static struct mgcp_trunk_config *reset_trunk;
 | 
			
		||||
static int reset_endpoints = 0;
 | 
			
		||||
static int daemonize = 0;
 | 
			
		||||
 | 
			
		||||
const char *openbsc_copyright =
 | 
			
		||||
const char *osmomgw_copyright =
 | 
			
		||||
	"Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n"
 | 
			
		||||
	"Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n"
 | 
			
		||||
	"Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n"
 | 
			
		||||
	"Copyright (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n"
 | 
			
		||||
	"Contributions by Pablo Neira Ayuso, Jacob Erlbeck, Neels Hofmeyr\r\n"
 | 
			
		||||
	"Philipp Maier\r\n\r\n"
 | 
			
		||||
	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
 | 
			
		||||
	"This is free software: you are free to change and redistribute it.\r\n"
 | 
			
		||||
	"There is NO WARRANTY, to the extent permitted by law.\r\n";
 | 
			
		||||
 | 
			
		||||
static char *config_file = "mgcp.cfg";
 | 
			
		||||
static char *config_file = "osmo-mgw.cfg";
 | 
			
		||||
 | 
			
		||||
/* used by msgb and mgcp */
 | 
			
		||||
void *tall_bsc_ctx = NULL;
 | 
			
		||||
@@ -102,7 +104,7 @@ static void handle_options(int argc, char **argv)
 | 
			
		||||
			{0, 0, 0, 0},
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		c = getopt_long(argc, argv, "hc:VD", long_options, &option_index);
 | 
			
		||||
		c = getopt_long(argc, argv, "hc:sVD", long_options, &option_index);
 | 
			
		||||
 | 
			
		||||
		if (c == -1)
 | 
			
		||||
			break;
 | 
			
		||||
@@ -132,10 +134,14 @@ static void handle_options(int argc, char **argv)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* simply remember this */
 | 
			
		||||
/* Callback function to be called when the RSIP ("Reset in Progress") mgcp
 | 
			
		||||
 * command is received */
 | 
			
		||||
static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg)
 | 
			
		||||
{
 | 
			
		||||
	/* Set flag so that, when read_call_agent() is called next time
 | 
			
		||||
	 * the reset can progress */
 | 
			
		||||
	reset_endpoints = 1;
 | 
			
		||||
 | 
			
		||||
	reset_trunk = tcfg;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -173,15 +179,19 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
		msgb_free(resp);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* reset endpoints */
 | 
			
		||||
	if (reset_endpoints) {
 | 
			
		||||
		LOGP(DLMGCP, LOGL_NOTICE,
 | 
			
		||||
		     "Asked to reset endpoints: %d/%d\n",
 | 
			
		||||
		     reset_trunk->trunk_nr, reset_trunk->trunk_type);
 | 
			
		||||
 | 
			
		||||
		/* reset flag */
 | 
			
		||||
		reset_endpoints = 0;
 | 
			
		||||
 | 
			
		||||
		/* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */
 | 
			
		||||
		/* Walk over all endpoints and trigger a release, this will release all
 | 
			
		||||
		 * endpoints, possible open connections are forcefully dropped */
 | 
			
		||||
		for (i = 1; i < reset_trunk->number_endpoints; ++i)
 | 
			
		||||
			mgcp_release_endp(&reset_trunk->endpoints[i]);
 | 
			
		||||
			mgcp_endp_release(&reset_trunk->endpoints[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
@@ -189,38 +199,38 @@ static int read_call_agent(struct osmo_fd *fd, unsigned int what)
 | 
			
		||||
 | 
			
		||||
int mgcp_vty_is_config_node(struct vty *vty, int node)
 | 
			
		||||
{
 | 
			
		||||
        switch (node) {
 | 
			
		||||
        case CONFIG_NODE:
 | 
			
		||||
                return 0;
 | 
			
		||||
	switch (node) {
 | 
			
		||||
	case CONFIG_NODE:
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
        default:
 | 
			
		||||
                return 1;
 | 
			
		||||
        }
 | 
			
		||||
	default:
 | 
			
		||||
		return 1;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mgcp_vty_go_parent(struct vty *vty)
 | 
			
		||||
{
 | 
			
		||||
        switch (vty->node) {
 | 
			
		||||
        case TRUNK_NODE:
 | 
			
		||||
                vty->node = MGCP_NODE;
 | 
			
		||||
                vty->index = NULL;
 | 
			
		||||
                break;
 | 
			
		||||
        case MGCP_NODE:
 | 
			
		||||
        default:
 | 
			
		||||
                if (mgcp_vty_is_config_node(vty, vty->node))
 | 
			
		||||
                        vty->node = CONFIG_NODE;
 | 
			
		||||
                else
 | 
			
		||||
                        vty->node = ENABLE_NODE;
 | 
			
		||||
	switch (vty->node) {
 | 
			
		||||
	case TRUNK_NODE:
 | 
			
		||||
		vty->node = MGCP_NODE;
 | 
			
		||||
		vty->index = NULL;
 | 
			
		||||
		break;
 | 
			
		||||
	case MGCP_NODE:
 | 
			
		||||
	default:
 | 
			
		||||
		if (mgcp_vty_is_config_node(vty, vty->node))
 | 
			
		||||
			vty->node = CONFIG_NODE;
 | 
			
		||||
		else
 | 
			
		||||
			vty->node = ENABLE_NODE;
 | 
			
		||||
 | 
			
		||||
                vty->index = NULL;
 | 
			
		||||
        }
 | 
			
		||||
		vty->index = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
        return vty->node;
 | 
			
		||||
	return vty->node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct vty_app_info vty_info = {
 | 
			
		||||
	.name 		= "OpenBSC MGCP",
 | 
			
		||||
	.name 		= "OsmoMGW",
 | 
			
		||||
	.version	= PACKAGE_VERSION,
 | 
			
		||||
	.go_parent_cb	= mgcp_vty_go_parent,
 | 
			
		||||
	.is_config_node	= mgcp_vty_is_config_node,
 | 
			
		||||
@@ -228,40 +238,47 @@ static struct vty_app_info vty_info = {
 | 
			
		||||
 | 
			
		||||
static const struct log_info_cat log_categories[] = {
 | 
			
		||||
	/* DLMGCP is provided by the MGCP library */
 | 
			
		||||
	[DRTP] = {
 | 
			
		||||
		  .name = "DRTP",
 | 
			
		||||
		  .description = "RTP stream handling",
 | 
			
		||||
		  .color = "\033[1;30m",
 | 
			
		||||
		  .enabled = 1,.loglevel = LOGL_NOTICE,
 | 
			
		||||
		  },
 | 
			
		||||
	[DIUUP] = {
 | 
			
		||||
		  .name = "DIUUP",
 | 
			
		||||
		  .description = "IuUP within RTP stream handling",
 | 
			
		||||
		  .color = "\033[1;31m",
 | 
			
		||||
		  .enabled = 1,.loglevel = LOGL_NOTICE,
 | 
			
		||||
		  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct log_info log_info = {
 | 
			
		||||
        .cat = log_categories,
 | 
			
		||||
        .num_cat = ARRAY_SIZE(log_categories),
 | 
			
		||||
	.cat = log_categories,
 | 
			
		||||
	.num_cat = ARRAY_SIZE(log_categories),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	struct sockaddr_in addr;
 | 
			
		||||
	int on = 1, rc;
 | 
			
		||||
	unsigned int flags;
 | 
			
		||||
	int rc;
 | 
			
		||||
 | 
			
		||||
	tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
 | 
			
		||||
	vty_info.tall_ctx = tall_bsc_ctx;
 | 
			
		||||
 | 
			
		||||
	msgb_talloc_ctx_init(tall_bsc_ctx, 0);
 | 
			
		||||
 | 
			
		||||
	osmo_init_ignore_signals();
 | 
			
		||||
	osmo_init_logging(&log_info);
 | 
			
		||||
	osmo_init_logging2(tall_bsc_ctx, &log_info);
 | 
			
		||||
 | 
			
		||||
	cfg = mgcp_config_alloc();
 | 
			
		||||
	if (!cfg)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
#ifdef BUILD_MGCP_TRANSCODING
 | 
			
		||||
	cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup;
 | 
			
		||||
	cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp;
 | 
			
		||||
	cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	cfg->trunk.force_realloc = 1;
 | 
			
		||||
 | 
			
		||||
	vty_info.copyright = openbsc_copyright;
 | 
			
		||||
	vty_info.copyright = osmomgw_copyright;
 | 
			
		||||
	vty_init(&vty_info);
 | 
			
		||||
	logging_vty_add_cmds(NULL);
 | 
			
		||||
	osmo_stats_vty_add_cmds(&log_info);
 | 
			
		||||
	logging_vty_add_cmds();
 | 
			
		||||
	osmo_talloc_vty_add_cmds();
 | 
			
		||||
	osmo_stats_vty_add_cmds();
 | 
			
		||||
	mgcp_vty_init();
 | 
			
		||||
 | 
			
		||||
	handle_options(argc, argv);
 | 
			
		||||
@@ -275,61 +292,37 @@ int main(int argc, char **argv)
 | 
			
		||||
 | 
			
		||||
	/* start telnet after reading config for vty_get_bind_addr() */
 | 
			
		||||
	rc = telnet_init_dynif(tall_bsc_ctx, NULL,
 | 
			
		||||
			       vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP);
 | 
			
		||||
			       vty_get_bind_addr(), OSMO_VTY_PORT_MGW);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		return rc;
 | 
			
		||||
 | 
			
		||||
	/* set some callbacks */
 | 
			
		||||
	/* Set the reset callback function. This functions is called when the
 | 
			
		||||
	 * mgcp-command "RSIP" (Reset in Progress) is received */
 | 
			
		||||
	cfg->reset_cb = mgcp_rsip_cb;
 | 
			
		||||
 | 
			
		||||
        /* we need to bind a socket */
 | 
			
		||||
        if (rc == 0) {
 | 
			
		||||
		cfg->gw_fd.bfd.when = BSC_FD_READ;
 | 
			
		||||
		cfg->gw_fd.bfd.cb = read_call_agent;
 | 
			
		||||
		cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0);
 | 
			
		||||
		if (cfg->gw_fd.bfd.fd < 0) {
 | 
			
		||||
			perror("Gateway failed to listen");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	/* we need to bind a socket */
 | 
			
		||||
	flags = OSMO_SOCK_F_BIND;
 | 
			
		||||
	if (cfg->call_agent_addr)
 | 
			
		||||
		flags |= OSMO_SOCK_F_CONNECT;
 | 
			
		||||
 | 
			
		||||
		setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
 | 
			
		||||
 | 
			
		||||
		memset(&addr, 0, sizeof(addr));
 | 
			
		||||
		addr.sin_family = AF_INET;
 | 
			
		||||
		addr.sin_port = htons(cfg->source_port);
 | 
			
		||||
		inet_aton(cfg->source_addr, &addr.sin_addr);
 | 
			
		||||
 | 
			
		||||
		if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 | 
			
		||||
			perror("Gateway failed to bind");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
 | 
			
		||||
		if (!cfg->gw_fd.bfd.data) {
 | 
			
		||||
			fprintf(stderr, "Gateway memory error.\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (cfg->call_agent_addr) {
 | 
			
		||||
			addr.sin_port = htons(2727);
 | 
			
		||||
			inet_aton(cfg->call_agent_addr, &addr.sin_addr);
 | 
			
		||||
			if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 | 
			
		||||
				LOGP(DLMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n",
 | 
			
		||||
				     cfg->call_agent_addr, errno);
 | 
			
		||||
				close(cfg->gw_fd.bfd.fd);
 | 
			
		||||
				cfg->gw_fd.bfd.fd = -1;
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) {
 | 
			
		||||
			LOGP(DLMGCP, LOGL_FATAL, "Failed to register the fd\n");
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP.\n");
 | 
			
		||||
	rc = osmo_sock_init2_ofd(&cfg->gw_fd.bfd, AF_INET, SOCK_DGRAM, IPPROTO_UDP,
 | 
			
		||||
				cfg->source_addr, cfg->source_port,
 | 
			
		||||
				cfg->call_agent_addr, cfg->call_agent_addr ? 2727 : 0, flags);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		perror("Gateway failed to bind");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cfg->gw_fd.bfd.cb = read_call_agent;
 | 
			
		||||
	cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg");
 | 
			
		||||
	if (!cfg->gw_fd.bfd.data) {
 | 
			
		||||
		fprintf(stderr, "Gateway memory error.\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGP(DLMGCP, LOGL_NOTICE, "Configured for MGCP, listen on %s:%u\n",
 | 
			
		||||
	     cfg->source_addr, cfg->source_port);
 | 
			
		||||
 | 
			
		||||
	/* initialisation */
 | 
			
		||||
	srand(time(NULL));
 | 
			
		||||
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
SUBDIRS = \
 | 
			
		||||
	legacy_mgcp \
 | 
			
		||||
	mgcp_client \
 | 
			
		||||
	mgcp \
 | 
			
		||||
	iuup \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
 | 
			
		||||
@@ -25,7 +26,6 @@ EXTRA_DIST = \
 | 
			
		||||
	testsuite.at \
 | 
			
		||||
	$(srcdir)/package.m4 \
 | 
			
		||||
	$(TESTSUITE) \
 | 
			
		||||
	vty_test_runner.py \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
TESTSUITE = $(srcdir)/testsuite
 | 
			
		||||
@@ -38,7 +38,6 @@ if ENABLE_EXT_TESTS
 | 
			
		||||
python-tests: $(BUILT_SOURCES)
 | 
			
		||||
	osmotestvty.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
 | 
			
		||||
	osmotestconfig.py -p $(abs_top_srcdir) -w $(abs_top_builddir) -v
 | 
			
		||||
	$(PYTHON) $(srcdir)/vty_test_runner.py -w $(abs_top_builddir) -v
 | 
			
		||||
else
 | 
			
		||||
python-tests: $(BUILT_SOURCES)
 | 
			
		||||
	echo "Not running python-based tests (determined at configure-time)"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
enable_nat_test='@osmo_ac_build_nat@'
 | 
			
		||||
enable_smpp_test='@osmo_ac_build_smpp@'
 | 
			
		||||
enable_bsc_test='@osmo_ac_build_bsc@'
 | 
			
		||||
enable_mgcp_transcoding_test='@osmo_ac_mgcp_transcoding@'
 | 
			
		||||
enable_sgsn_test='@found_libgtp_and_libcares@'
 | 
			
		||||
enable_oap_test='@found_libgtp_and_libcares@'
 | 
			
		||||
enable_gtphub_test='@found_libgtp_and_libcares@'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								tests/iuup/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								tests/iuup/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	$(all_includes) \
 | 
			
		||||
	-I$(top_srcdir)/include \
 | 
			
		||||
	-I$(top_srcdir) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = \
 | 
			
		||||
	-Wall \
 | 
			
		||||
	-ggdb3 \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOGSM_CFLAGS) \
 | 
			
		||||
	$(LIBOSMONETIF_CFLAGS) \
 | 
			
		||||
	$(COVERAGE_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = \
 | 
			
		||||
	$(COVERAGE_LDFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	iuup_test.ok \
 | 
			
		||||
	iuup_test.err \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = \
 | 
			
		||||
	iuup_test \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
iuup_test_SOURCES = \
 | 
			
		||||
	iuup_test.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
iuup_test_LDADD = \
 | 
			
		||||
	$(top_builddir)/src/libosmo-mgcp/libosmo-mgcp.a \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(LIBOSMOGSM_LIBS) \
 | 
			
		||||
	$(LIBRARY_DL) \
 | 
			
		||||
	$(LIBOSMONETIF_LIBS) \
 | 
			
		||||
	-lm  \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
update_exp:
 | 
			
		||||
	$(builddir)/iuup_test >$(srcdir)/iuup_test.ok 2>$(srcdir)/iuup_test.err
 | 
			
		||||
							
								
								
									
										156
									
								
								tests/iuup/iuup_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								tests/iuup/iuup_test.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,156 @@
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/mgcp/iuup_cn_node.h>
 | 
			
		||||
#include <osmocom/mgcp/iuup_protocol.h>
 | 
			
		||||
 | 
			
		||||
void *ctx = NULL;
 | 
			
		||||
 | 
			
		||||
static const char *dump(struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	return osmo_hexdump_nospc(msg->data, msg->len);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct msgb *msgb_from_hex(const char *label, const char *hex)
 | 
			
		||||
{
 | 
			
		||||
	struct msgb *msg = msgb_alloc_headroom(4096 + OSMO_IUUP_HEADROOM,
 | 
			
		||||
					       OSMO_IUUP_HEADROOM, label);
 | 
			
		||||
	unsigned char *rc;
 | 
			
		||||
	msg->l2h = msg->data;
 | 
			
		||||
	rc = msgb_put(msg, osmo_hexparse(hex, msg->data, msgb_tailroom(msg)));
 | 
			
		||||
	OSMO_ASSERT(rc == msg->l2h);
 | 
			
		||||
	return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *expect_rx_payload = NULL;
 | 
			
		||||
int rx_payload(struct msgb *msg, void *node_priv)
 | 
			
		||||
{
 | 
			
		||||
	printf("rx_payload() invoked by iuup_cn!\n");
 | 
			
		||||
	printf("        [IuUP] -RTP->\n");
 | 
			
		||||
	printf("%s\n", dump(msg));
 | 
			
		||||
	printf("node_priv=%p\n", node_priv);
 | 
			
		||||
	if (!expect_rx_payload) {
 | 
			
		||||
		printf("ERROR: did not expect rx_payload()\n");
 | 
			
		||||
		exit(-1);
 | 
			
		||||
	} else if (strcmp(expect_rx_payload, dump(msg))) {
 | 
			
		||||
		printf("ERROR: mismatches expected msg %s\n", expect_rx_payload);
 | 
			
		||||
		exit(-1);
 | 
			
		||||
	} else
 | 
			
		||||
		printf("ok: matches expected msg\n");
 | 
			
		||||
	expect_rx_payload = NULL;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *expect_tx_msg = NULL;
 | 
			
		||||
int tx_msg(struct msgb *msg, void *node_priv)
 | 
			
		||||
{
 | 
			
		||||
	printf("tx_msg() invoked by iuup_cn!\n");
 | 
			
		||||
	printf(" <-PDU- [IuUP]\n");
 | 
			
		||||
	printf("%s\n", dump(msg));
 | 
			
		||||
	printf("node_priv=%p\n", node_priv);
 | 
			
		||||
	if (!expect_tx_msg) {
 | 
			
		||||
		printf("ERROR: did not expect tx_msg()\n");
 | 
			
		||||
		exit(-1);
 | 
			
		||||
	} else if (strcmp(expect_tx_msg, dump(msg))) {
 | 
			
		||||
		printf("ERROR: mismatches expected msg %s\n", expect_tx_msg);
 | 
			
		||||
		exit(-1);
 | 
			
		||||
	} else
 | 
			
		||||
		printf("ok: matches expected msg\n");
 | 
			
		||||
	expect_tx_msg = NULL;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	printf(" -PDU-> [IuUP]\n");
 | 
			
		||||
	printf("%s\n", dump(msg));
 | 
			
		||||
	rc = osmo_iuup_cn_rx_pdu(cn, msg);
 | 
			
		||||
	printf("rc=%d\n", rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	int rc;
 | 
			
		||||
	printf("        [IuUP] <-RTP-\n");
 | 
			
		||||
	printf("%s\n", dump(msg));
 | 
			
		||||
	rc = osmo_iuup_cn_tx_payload(cn, msg);
 | 
			
		||||
	printf("rc=%d\n", rc);
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void test_cn_session()
 | 
			
		||||
{
 | 
			
		||||
	void *node_priv = (void*)0x2342;
 | 
			
		||||
 | 
			
		||||
	struct osmo_iuup_cn_cfg cfg = {
 | 
			
		||||
		.node_priv = node_priv,
 | 
			
		||||
		.rx_payload = rx_payload,
 | 
			
		||||
		.tx_msg = tx_msg,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	struct osmo_iuup_cn *cn = osmo_iuup_cn_init(ctx, &cfg, __func__);
 | 
			
		||||
	OSMO_ASSERT(cn);
 | 
			
		||||
 | 
			
		||||
	printf("\nSend IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack\n");
 | 
			
		||||
	expect_tx_msg = "8060dc5219495e3f00010111" /* RTP header */
 | 
			
		||||
			"e4002400"; /* IuUP Init Ack */
 | 
			
		||||
	rx_pdu(cn,
 | 
			
		||||
	       msgb_from_hex("IuUP-Init",
 | 
			
		||||
			     "8060dc5219495e3f00010111" /* <- RTP header */
 | 
			
		||||
			     "e000df99" /* <- IuUP header */
 | 
			
		||||
			     "160051673c01270000820000001710000100" /* IuUP params */));
 | 
			
		||||
 | 
			
		||||
#define RTP_HEADER "8060944c6256042c00010102"
 | 
			
		||||
#define IUUP_HEADER "0100e2b3"
 | 
			
		||||
#define RTP_PAYLOAD "6cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0"
 | 
			
		||||
	printf("\nReceive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet\n");
 | 
			
		||||
	printf("i.e. should strip away " IUUP_HEADER "\n");
 | 
			
		||||
	expect_rx_payload = RTP_HEADER "f03c" RTP_PAYLOAD;
 | 
			
		||||
	rx_pdu(cn,
 | 
			
		||||
	       msgb_from_hex("IuUP-Data",
 | 
			
		||||
			     RTP_HEADER IUUP_HEADER RTP_PAYLOAD));
 | 
			
		||||
 | 
			
		||||
	printf("\nTransmit RTP. Expecting tx_msg() with inserted IuUP header\n");
 | 
			
		||||
	expect_tx_msg = RTP_HEADER "000002b3" RTP_PAYLOAD;
 | 
			
		||||
	tx_payload(cn,
 | 
			
		||||
		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
			
		||||
 | 
			
		||||
	printf("\nMore RTP, each time the Frame Nr advances, causing a new header CRC.\n");
 | 
			
		||||
	expect_tx_msg = RTP_HEADER "0100e2b3" RTP_PAYLOAD;
 | 
			
		||||
	tx_payload(cn,
 | 
			
		||||
		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
			
		||||
	expect_tx_msg = RTP_HEADER "02007eb3" RTP_PAYLOAD;
 | 
			
		||||
	tx_payload(cn,
 | 
			
		||||
		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
			
		||||
	expect_tx_msg = RTP_HEADER "03009eb3" RTP_PAYLOAD;
 | 
			
		||||
	tx_payload(cn,
 | 
			
		||||
		   msgb_from_hex("RTP data", RTP_HEADER "f03c" RTP_PAYLOAD));
 | 
			
		||||
 | 
			
		||||
	printf("All done.\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct log_info_cat log_categories[] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const struct log_info log_info = {
 | 
			
		||||
	.cat = log_categories,
 | 
			
		||||
	.num_cat = ARRAY_SIZE(log_categories),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int main(void)
 | 
			
		||||
{
 | 
			
		||||
	ctx = talloc_named_const(NULL, 0, __FILE__);
 | 
			
		||||
	void *msgb_ctx = msgb_talloc_ctx_init(ctx, 0);
 | 
			
		||||
	osmo_init_logging2(ctx, &log_info);
 | 
			
		||||
 | 
			
		||||
	test_cn_session();
 | 
			
		||||
 | 
			
		||||
	talloc_free(msgb_ctx);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user