Compare commits
4230 Commits
merged_fro
...
STABLE-2_0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
216bf89491 | ||
|
|
a68eddbd47 | ||
|
|
d5dd5241e7 | ||
|
|
c4eb52dcff | ||
|
|
1e6c4ac017 | ||
|
|
b978d17ca0 | ||
|
|
dde55c6c0e | ||
|
|
5d22679c67 | ||
|
|
132dafa3fc | ||
|
|
54d76ffd6d | ||
|
|
b6f32caaad | ||
|
|
1c9e603299 | ||
|
|
1a7ba24d13 | ||
|
|
d4384cfac4 | ||
|
|
a82ec4499f | ||
|
|
7d8989e3ea | ||
|
|
5079e4552c | ||
|
|
e040132d92 | ||
|
|
760281207e | ||
|
|
20fde0be6c | ||
|
|
67895e7bdd | ||
|
|
801f26ee0c | ||
|
|
4dffe521a3 | ||
|
|
f5f8ab5acf | ||
|
|
e3a9f01fe4 | ||
|
|
37d5691b19 | ||
|
|
725feb0d4a | ||
|
|
6edde498e3 | ||
|
|
d5bfec2e52 | ||
|
|
576f49ee2b | ||
|
|
697be5c2c3 | ||
|
|
c9cfbe27c1 | ||
|
|
0a343948d9 | ||
|
|
ed239c4e71 | ||
|
|
695c81762c | ||
|
|
f6e27940bd | ||
|
|
9cd555c51a | ||
|
|
4e74ae4bc9 | ||
|
|
5477aa5a42 | ||
|
|
70ccea9207 | ||
|
|
69be49fdc3 | ||
|
|
740182de3c | ||
|
|
ad3530ee10 | ||
|
|
86a92543de | ||
|
|
2afc2a52d5 | ||
|
|
a1c0a0185b | ||
|
|
05419912e0 | ||
|
|
13fb616bb2 | ||
|
|
0c06073819 | ||
|
|
50e09ccd2c | ||
|
|
fa211096c2 | ||
|
|
87172d6d35 | ||
|
|
60a507f88b | ||
|
|
0d7805a86a | ||
|
|
9ba9dee2aa | ||
|
|
95754ba95a | ||
|
|
bef7873042 | ||
|
|
ec236da972 | ||
|
|
ee27daffc4 | ||
|
|
cdc97d2779 | ||
|
|
d9c6badc55 | ||
|
|
30251b1404 | ||
|
|
d1ed89b2e4 | ||
|
|
81549578bf | ||
|
|
f8683499a6 | ||
|
|
1e5efee7cb | ||
|
|
b040544628 | ||
|
|
eb77c839fc | ||
|
|
5e15125b3d | ||
|
|
8f8f56914b | ||
|
|
b33070e0cf | ||
|
|
8c52afb6ca | ||
|
|
df5a79966d | ||
|
|
d7f8d33506 | ||
|
|
db9c866fff | ||
|
|
149701b347 | ||
|
|
682b82aad8 | ||
|
|
65796cd827 | ||
|
|
40846260b5 | ||
|
|
af04864094 | ||
|
|
5ddd2aef4b | ||
|
|
17e6c9dd02 | ||
|
|
0e7f48d81c | ||
|
|
461b3531c7 | ||
|
|
4144d54642 | ||
|
|
837b7b3f98 | ||
|
|
1f68b32485 | ||
|
|
f8d19e28de | ||
|
|
f3cec74bd6 | ||
|
|
87e815030c | ||
|
|
78498981e2 | ||
|
|
633696c153 | ||
|
|
0e2354e658 | ||
|
|
5d811d799c | ||
|
|
6abcd00f71 | ||
|
|
97b774ceb9 | ||
|
|
2f085aa441 | ||
|
|
354e385453 | ||
|
|
c7c6b7ce93 | ||
|
|
c25de8f317 | ||
|
|
c9bae5ea9b | ||
|
|
dbd847b70c | ||
|
|
19e2780656 | ||
|
|
eb3a08308d | ||
|
|
470dae613e | ||
|
|
22907c7b27 | ||
|
|
623f9ce046 | ||
|
|
4d4710dadf | ||
|
|
2facd2d64d | ||
|
|
ee4cd45c98 | ||
|
|
58c8e0f8ca | ||
|
|
8dbd1abdc4 | ||
|
|
ef0dc65515 | ||
|
|
8cd43a3dd9 | ||
|
|
4a73bcbc65 | ||
|
|
f38705c38c | ||
|
|
b7c2553b46 | ||
|
|
f8a95aa27f | ||
|
|
d66442ba91 | ||
|
|
aeae4e91db | ||
|
|
34682facd1 | ||
|
|
ac6b64cf66 | ||
|
|
b944ceb89d | ||
|
|
ab8a1a0430 | ||
|
|
4325aca0f7 | ||
|
|
23147b0e21 | ||
|
|
9078f31544 | ||
|
|
a08ed9148d | ||
|
|
8d45162a59 | ||
|
|
f5135b05d9 | ||
|
|
2137f49d32 | ||
|
|
aef3d2cb87 | ||
|
|
840d1e60fa | ||
|
|
198fa5dbfa | ||
|
|
ef827e85b8 | ||
|
|
e545262d98 | ||
|
|
fb62e9350f | ||
|
|
db6aa82a10 | ||
|
|
c9dff6b5aa | ||
|
|
4b45baee10 | ||
|
|
05a6d82fa1 | ||
|
|
0d510dd66d | ||
|
|
537bd836c9 | ||
|
|
57468b8a30 | ||
|
|
452f5d6296 | ||
|
|
2980a12373 | ||
|
|
89aa4e7d79 | ||
|
|
3e23eb764b | ||
|
|
aeb3834219 | ||
|
|
475d49440c | ||
|
|
a2ca85a260 | ||
|
|
4c390ad39c | ||
|
|
a1db05c11e | ||
|
|
bf5866b27d | ||
|
|
298ec340e5 | ||
|
|
1ba0e17157 | ||
|
|
43ddf6eee6 | ||
|
|
8426dfa14d | ||
|
|
6fc7f84497 | ||
|
|
831b7fc7d3 | ||
|
|
d9b0236525 | ||
|
|
e78bc41116 | ||
|
|
c719c466b3 | ||
|
|
31778193da | ||
|
|
6caa7b9927 | ||
|
|
979bee386c | ||
|
|
d2f52e19d5 | ||
|
|
56dc574bed | ||
|
|
e4c74109cd | ||
|
|
fd5b34ae43 | ||
|
|
306171c93b | ||
|
|
d99d91dae9 | ||
|
|
fc54556d80 | ||
|
|
0d5eea288c | ||
|
|
4456c7d230 | ||
|
|
f170dde1c6 | ||
|
|
bae4d6398c | ||
|
|
18136c047b | ||
|
|
d9eaf6f310 | ||
|
|
42c193821c | ||
|
|
1c5c96a50a | ||
|
|
40f2bed5c4 | ||
|
|
21c99b6ebb | ||
|
|
a032ccafe7 | ||
|
|
4f4d16260f | ||
|
|
6b1e1af3d1 | ||
|
|
b3bae1b44c | ||
|
|
730080f20e | ||
|
|
a189941da6 | ||
|
|
f93a6e3310 | ||
|
|
5e36815867 | ||
|
|
01d8e5013f | ||
|
|
6688033bc4 | ||
|
|
ed566cceaa | ||
|
|
55d05092ef | ||
|
|
0e9ef19cfb | ||
|
|
fefb782bfe | ||
|
|
12609c951c | ||
|
|
64f79e7ffc | ||
|
|
ebd1bf8a49 | ||
|
|
4a7dafc6a6 | ||
|
|
1e6f33d44b | ||
|
|
96c3c48ba7 | ||
|
|
14fb48cd7a | ||
|
|
9725a496b5 | ||
|
|
967516aa40 | ||
|
|
e844159f0a | ||
|
|
11386a26d9 | ||
|
|
8eb9db18a2 | ||
|
|
43d6812b3e | ||
|
|
7c0d952379 | ||
|
|
c4e3be814f | ||
|
|
ad357a4c89 | ||
|
|
2d503f4433 | ||
|
|
5df88220c4 | ||
|
|
458211c2af | ||
|
|
84e139f20c | ||
|
|
81c68f529b | ||
|
|
f299b4b7cf | ||
|
|
8d68400387 | ||
|
|
d89fa2dcbc | ||
|
|
16293125a6 | ||
|
|
e24fde05db | ||
|
|
ef31afb921 | ||
|
|
a8c8e08984 | ||
|
|
97fae7e41b | ||
|
|
7f60cb3889 | ||
|
|
ebf7959880 | ||
|
|
0c7a59b5db | ||
|
|
c9ad58308a | ||
|
|
a262a2f252 | ||
|
|
fa6a2d48cc | ||
|
|
c62bfd8146 | ||
|
|
1db9631e7d | ||
|
|
c8c804140d | ||
|
|
ee815e4e7a | ||
|
|
c28fb298b7 | ||
|
|
93b286e508 | ||
|
|
2a90f33757 | ||
|
|
efb7b3d5f8 | ||
|
|
02d51e3ac5 | ||
|
|
fec657bb38 | ||
|
|
858287fc3a | ||
|
|
af6b707e9a | ||
|
|
ab72ed8517 | ||
|
|
482a4d2ce9 | ||
|
|
c61c8f3766 | ||
|
|
af48bec63c | ||
|
|
39ac8e2c57 | ||
|
|
52449e12c0 | ||
|
|
2335c1a73c | ||
|
|
ebe0e6f98d | ||
|
|
4d85def20a | ||
|
|
b472648e40 | ||
|
|
a2894ede1c | ||
|
|
306113c8c3 | ||
|
|
4919932c49 | ||
|
|
4af297fc20 | ||
|
|
4e1f8effaf | ||
|
|
5493220c93 | ||
|
|
ce6ea0df15 | ||
|
|
6dcb2b2415 | ||
|
|
36b9caed23 | ||
|
|
13251526fa | ||
|
|
b34baff546 | ||
|
|
6f62fe5998 | ||
|
|
dc7340bbd5 | ||
|
|
d95ab511d0 | ||
|
|
bf3e8e6a48 | ||
|
|
3194c9c4cf | ||
|
|
18fcc1d504 | ||
|
|
5c0944e01a | ||
|
|
5a09fd3e35 | ||
|
|
0005b7c2d0 | ||
|
|
6ba03d543f | ||
|
|
61dae47a71 | ||
|
|
85ab39985a | ||
|
|
e5284ec616 | ||
|
|
4b41ef551e | ||
|
|
a2fd68098e | ||
|
|
1631307bb3 | ||
|
|
10332773ff | ||
|
|
fa568f7750 | ||
|
|
282b8a2b6c | ||
|
|
b1dfd00f92 | ||
|
|
eba6ae0122 | ||
|
|
219438fb24 | ||
|
|
e4c01a064e | ||
|
|
08378b7d4b | ||
|
|
fc66fb830c | ||
|
|
fa8797b86d | ||
|
|
953dd5b628 | ||
|
|
455a41822e | ||
|
|
1ea1026961 | ||
|
|
09c22e13fe | ||
|
|
7c02a85424 | ||
|
|
d15ebc6a4c | ||
|
|
f185104ac6 | ||
|
|
5811948b0a | ||
|
|
dd0779c204 | ||
|
|
898e69118a | ||
|
|
f55c0e7864 | ||
|
|
3ca4eb5cd8 | ||
|
|
6af213787a | ||
|
|
0a7db8a9a5 | ||
|
|
0673fcdb44 | ||
|
|
980a919c9a | ||
|
|
8d07629b71 | ||
|
|
cf66233873 | ||
|
|
b94c8ee76f | ||
|
|
73131b1677 | ||
|
|
87855b0e0e | ||
|
|
2acfa0ebe7 | ||
|
|
a20cdc597a | ||
|
|
70ee63ef3d | ||
|
|
c2a5480ac7 | ||
|
|
01561b26ef | ||
|
|
9b47b6393b | ||
|
|
e8d8c5dcc9 | ||
|
|
bae67915ab | ||
|
|
5f774270b6 | ||
|
|
02221cf5dc | ||
|
|
6383ef88b4 | ||
|
|
dd110309e5 | ||
|
|
4c06a737a4 | ||
|
|
da83946e75 | ||
|
|
3d379c97ec | ||
|
|
fd096a5769 | ||
|
|
15b75555a6 | ||
|
|
018294d287 | ||
|
|
247d2e97a0 | ||
|
|
92f385aaed | ||
|
|
2f7e6d0661 | ||
|
|
e4d75a75ea | ||
|
|
501cfbe02b | ||
|
|
a7979d7d24 | ||
|
|
52d6d696ae | ||
|
|
e76b8b2551 | ||
|
|
d00609257b | ||
|
|
6cc7f38c99 | ||
|
|
2c2d11fa4d | ||
|
|
4cb7e31d2d | ||
|
|
0c7d015ec4 | ||
|
|
4b67c582f6 | ||
|
|
5f9c944da4 | ||
|
|
b7da649944 | ||
|
|
90fba8773f | ||
|
|
3f0dae29e9 | ||
|
|
9fb9033815 | ||
|
|
8c620d9206 | ||
|
|
b4efa33b7c | ||
|
|
71dc8f8f53 | ||
|
|
161ee4f4b2 | ||
|
|
eac1005c9c | ||
|
|
b0284a6927 | ||
|
|
bdaec1691e | ||
|
|
5bcaefddd4 | ||
|
|
f7e12d835c | ||
|
|
93ad162aa6 | ||
|
|
f322e782f8 | ||
|
|
23cf45d252 | ||
|
|
56102c1b1c | ||
|
|
53dc94d570 | ||
|
|
a326b057b3 | ||
|
|
1bb2539f74 | ||
|
|
b14032c531 | ||
|
|
70927892e1 | ||
|
|
0e6f2049ad | ||
|
|
b2b1ec14f5 | ||
|
|
a39c040571 | ||
|
|
fb7998699d | ||
|
|
d89378e3f8 | ||
|
|
63fdb3bb62 | ||
|
|
31f941e172 | ||
|
|
8ece46d5d2 | ||
|
|
0cb1d1144d | ||
|
|
aece68639a | ||
|
|
eb3261d6e0 | ||
|
|
9305bf2ace | ||
|
|
0b5c393361 | ||
|
|
6c7eef7ac0 | ||
|
|
ef5c1b6590 | ||
|
|
fc7aa7a247 | ||
|
|
ccc830c99c | ||
|
|
af97f9b239 | ||
|
|
6618189342 | ||
|
|
6b524367a3 | ||
|
|
1993b0257c | ||
|
|
6bce7509f7 | ||
|
|
7e4d934f7a | ||
|
|
4a7d07a2e0 | ||
|
|
765181a97c | ||
|
|
7b770dd9b6 | ||
|
|
7f43fcab71 | ||
|
|
c7e20150f0 | ||
|
|
c6831648e2 | ||
|
|
8a9de94b1f | ||
|
|
8140c77d7d | ||
|
|
67d674a59c | ||
|
|
dc0859b8d1 | ||
|
|
9c10daba93 | ||
|
|
0fea2bc02e | ||
|
|
ce19c59bb2 | ||
|
|
8643782e5d | ||
|
|
bd79f6c055 | ||
|
|
40bc80b551 | ||
|
|
e030118750 | ||
|
|
59295be4ef | ||
|
|
10acd8303d | ||
|
|
73ee4cbbf7 | ||
|
|
f6468510c6 | ||
|
|
a62e4452a2 | ||
|
|
168ad22761 | ||
|
|
fb75f48751 | ||
|
|
ca71eea56f | ||
|
|
00598b0b46 | ||
|
|
4f5ff37c5d | ||
|
|
80be1a6bf8 | ||
|
|
d75ece2505 | ||
|
|
28dd0813ab | ||
|
|
194d624077 | ||
|
|
de9054cb7a | ||
|
|
413eeef5fa | ||
|
|
6e6ce4fb78 | ||
|
|
f98c3dd4b5 | ||
|
|
806298583b | ||
|
|
764343ebc7 | ||
|
|
523b11e664 | ||
|
|
d43c092f17 | ||
|
|
9cc3fb2e63 | ||
|
|
802a4f2a14 | ||
|
|
3ad2ad2329 | ||
|
|
299a19e135 | ||
|
|
e8ffac852e | ||
|
|
98d58ffd59 | ||
|
|
97b8e88e69 | ||
|
|
24a339a609 | ||
|
|
86e419425b | ||
|
|
d0a79ff085 | ||
|
|
dff46e3816 | ||
|
|
b06f14c11f | ||
|
|
579fffd2ec | ||
|
|
5d4c1432c2 | ||
|
|
6adeb706a6 | ||
|
|
c641ae3d3d | ||
|
|
72208cddfa | ||
|
|
7037b340c1 | ||
|
|
06c7404461 | ||
|
|
6c0d78caaa | ||
|
|
4ec3d29168 | ||
|
|
09636c5b92 | ||
|
|
cb99ca099e | ||
|
|
2f950a7dcc | ||
|
|
a324c7a8e4 | ||
|
|
4ea1d62d45 | ||
|
|
421dab87e8 | ||
|
|
01e5d6d819 | ||
|
|
18c332ae51 | ||
|
|
9a355502e1 | ||
|
|
52da49cd81 | ||
|
|
288fc8ede3 | ||
|
|
6293a835e9 | ||
|
|
6af4215f27 | ||
|
|
2df636fe77 | ||
|
|
e302b1bbd2 | ||
|
|
c483520081 | ||
|
|
959042aa88 | ||
|
|
a463119597 | ||
|
|
e0918d706e | ||
|
|
b939e9536d | ||
|
|
e9b0003085 | ||
|
|
087ecab891 | ||
|
|
212eacd9d6 | ||
|
|
dcd52510ce | ||
|
|
98e92f6550 | ||
|
|
b91e47b518 | ||
|
|
811b237bd7 | ||
|
|
7e3de89646 | ||
|
|
4b136d631a | ||
|
|
5a1b9bf808 | ||
|
|
14ca418ac0 | ||
|
|
cb5f7859fd | ||
|
|
2fdea8b79b | ||
|
|
2e26fc9224 | ||
|
|
fd83f4fb22 | ||
|
|
d362e167c0 | ||
|
|
a12c149093 | ||
|
|
afa2a96085 | ||
|
|
9d0546839c | ||
|
|
28aaf2caf4 | ||
|
|
40e9eae791 | ||
|
|
f0dbba6406 | ||
|
|
a99fe1fa32 | ||
|
|
e7069d6e82 | ||
|
|
b97c4d96e2 | ||
|
|
f21aede031 | ||
|
|
c1c3d0e1ba | ||
|
|
50b5b4c4dd | ||
|
|
b9b36084a5 | ||
|
|
f226e107a6 | ||
|
|
d4f824398c | ||
|
|
b438a0d6fd | ||
|
|
7c368b7f36 | ||
|
|
06782c699c | ||
|
|
42dfa71f97 | ||
|
|
d3e55185c6 | ||
|
|
e6bc591a1e | ||
|
|
d31d2ee882 | ||
|
|
91b5d8ad1e | ||
|
|
aecbefc728 | ||
|
|
ecbe45bf43 | ||
|
|
a0e8c2dd8f | ||
|
|
500e748888 | ||
|
|
4047702928 | ||
|
|
5d356c96f5 | ||
|
|
39545d2c6d | ||
|
|
e40175ef05 | ||
|
|
2d7a6a5d94 | ||
|
|
c164869f6d | ||
|
|
236bc19422 | ||
|
|
ddac5b1895 | ||
|
|
9a70715371 | ||
|
|
96296947fc | ||
|
|
8b9886bfe2 | ||
|
|
9015c28406 | ||
|
|
b77fcef1a5 | ||
|
|
6fb074874b | ||
|
|
3d684cda23 | ||
|
|
71ca26b212 | ||
|
|
4a5422cd11 | ||
|
|
38c0255fa2 | ||
|
|
102f5882d5 | ||
|
|
19e2b15c41 | ||
|
|
a929f418e7 | ||
|
|
0116c3cd76 | ||
|
|
2c1bd363e6 | ||
|
|
efd3fb8f4e | ||
|
|
94e502fc8a | ||
|
|
224d5a9f33 | ||
|
|
d133999e1d | ||
|
|
98c741976b | ||
|
|
502f08cb92 | ||
|
|
1af40e7de2 | ||
|
|
75c5829a57 | ||
|
|
8dc77ef558 | ||
|
|
cfe5182380 | ||
|
|
2040f4f0b1 | ||
|
|
7b4bd3343b | ||
|
|
1d108b400f | ||
|
|
42d50eba4e | ||
|
|
7df5496e7b | ||
|
|
c751802fad | ||
|
|
6d95a34971 | ||
|
|
61e067b98a | ||
|
|
fda778f6b6 | ||
|
|
d98e25a783 | ||
|
|
4e241fbf14 | ||
|
|
b9a2ee8aaa | ||
|
|
25652254a5 | ||
|
|
5e7b343d52 | ||
|
|
606b6990ff | ||
|
|
270378387c | ||
|
|
9ddde3ef4a | ||
|
|
5a056cc861 | ||
|
|
5d9f59daa4 | ||
|
|
076f1ee89e | ||
|
|
010b0e2972 | ||
|
|
952da88b84 | ||
|
|
9be133777b | ||
|
|
6be7e221a5 | ||
|
|
a364427ae5 | ||
|
|
451277e7f0 | ||
|
|
51f3cdfd5a | ||
|
|
344de0e119 | ||
|
|
bfd3baa20e | ||
|
|
631c458c55 | ||
|
|
6c8c3fd48c | ||
|
|
90a656ed78 | ||
|
|
f4bf0dff85 | ||
|
|
59af802fb6 | ||
|
|
ebd103775d | ||
|
|
bd177ff38f | ||
|
|
ac21a5f370 | ||
|
|
ac0af0d7c7 | ||
|
|
8e7b8a32f9 | ||
|
|
ab989c3551 | ||
|
|
8c0f620d83 | ||
|
|
7e9377a9c7 | ||
|
|
9f69bf82df | ||
|
|
c6949d88d9 | ||
|
|
45c6279bb0 | ||
|
|
b9389c6eaf | ||
|
|
b08f73be44 | ||
|
|
33fc20cca2 | ||
|
|
5df4f70a96 | ||
|
|
9f3aff0cdf | ||
|
|
3417a02b25 | ||
|
|
1dcd5d31d7 | ||
|
|
73b8026cb9 | ||
|
|
fc2701ae03 | ||
|
|
4a6c444a17 | ||
|
|
05961a27cf | ||
|
|
93665a7659 | ||
|
|
49badf39a1 | ||
|
|
33955c636d | ||
|
|
2e05856f90 | ||
|
|
30e6f908ee | ||
|
|
e653ac9db7 | ||
|
|
21354af674 | ||
|
|
c981b726f8 | ||
|
|
21e6e1c824 | ||
|
|
b07031479c | ||
|
|
475be665ff | ||
|
|
3d38af5174 | ||
|
|
b28a80375b | ||
|
|
a5d4a27ae1 | ||
|
|
afd9ee9147 | ||
|
|
afd1bb938e | ||
|
|
025d5591eb | ||
|
|
7d77a52ea5 | ||
|
|
2b5250dd9d | ||
|
|
8ce49499ae | ||
|
|
5a123a3405 | ||
|
|
0d0b935d3d | ||
|
|
2be86fbc3e | ||
|
|
bb908366a5 | ||
|
|
73d8f14ebd | ||
|
|
ffa340a68c | ||
|
|
b8b83c2994 | ||
|
|
4d1a2e6699 | ||
|
|
15fbfb7363 | ||
|
|
54f7cae12c | ||
|
|
76d25befb4 | ||
|
|
749f7fae1a | ||
|
|
61d07f88f0 | ||
|
|
8ac1b4825a | ||
|
|
87354abf0f | ||
|
|
0b6370b9b8 | ||
|
|
92f5da8412 | ||
|
|
309e072238 | ||
|
|
ab2c65549d | ||
|
|
ca1b8288de | ||
|
|
8c7f513f4d | ||
|
|
16c70dd60e | ||
|
|
03e9c88488 | ||
|
|
0731eda0b3 | ||
|
|
083c6123d6 | ||
|
|
12a4cae404 | ||
|
|
3a26e8b6cc | ||
|
|
28652d4d51 | ||
|
|
b67427bd3b | ||
|
|
3a7c998f91 | ||
|
|
141aeaab3d | ||
|
|
c8b65f6c1d | ||
|
|
d70d9bf866 | ||
|
|
4bd57882b0 | ||
|
|
52fdc6bb3e | ||
|
|
d0c3baf340 | ||
|
|
6f43057662 | ||
|
|
d8784a77b7 | ||
|
|
27f03798b9 | ||
|
|
5b0a47ca87 | ||
|
|
9b0a0841e9 | ||
|
|
0b4bc5ea31 | ||
|
|
cab341976e | ||
|
|
52a3bf5d43 | ||
|
|
63c2dc926e | ||
|
|
cc477361ad | ||
|
|
377885581f | ||
|
|
7321420d8c | ||
|
|
9466d64a9a | ||
|
|
d929a4b17e | ||
|
|
945607e094 | ||
|
|
7721b20179 | ||
|
|
c6b742812d | ||
|
|
85d5fbdc7f | ||
|
|
449fc98a55 | ||
|
|
7e7f2f31ff | ||
|
|
a1fc91afa8 | ||
|
|
c39ef75cb3 | ||
|
|
7bda462031 | ||
|
|
f97bf85fd6 | ||
|
|
a9c7ead6b7 | ||
|
|
dd80759bb9 | ||
|
|
a1c78ea7bf | ||
|
|
5794ac2340 | ||
|
|
44e1a2d8e2 | ||
|
|
3a62a45bcd | ||
|
|
149bb36805 | ||
|
|
93ccba9bcb | ||
|
|
b24e5cd1c8 | ||
|
|
7fbb5fc82a | ||
|
|
464a8c9794 | ||
|
|
7aea739f87 | ||
|
|
852d5b9c80 | ||
|
|
b4d7238eb8 | ||
|
|
ee87d28252 | ||
|
|
6ffe12cede | ||
|
|
d6adc1f6cb | ||
|
|
5f8b5cbb20 | ||
|
|
7b9cb98bbb | ||
|
|
222eb25eb5 | ||
|
|
dbfdb13166 | ||
|
|
d38cdccbcd | ||
|
|
2575c7fb5f | ||
|
|
ea174560b1 | ||
|
|
bc51dddcaf | ||
|
|
f09dec5fb7 | ||
|
|
8e8571da6a | ||
|
|
0d576aa521 | ||
|
|
0e1b401abe | ||
|
|
bf7fc41e0f | ||
|
|
90bc1da434 | ||
|
|
dc98e31e4d | ||
|
|
1d7996dc47 | ||
|
|
2b3db52c70 | ||
|
|
b65a22b32a | ||
|
|
b849e52c17 | ||
|
|
588b11185a | ||
|
|
dfc963880a | ||
|
|
00975769a0 | ||
|
|
f7a3d6cca5 | ||
|
|
8f71795844 | ||
|
|
8106413644 | ||
|
|
5e472badf1 | ||
|
|
2a2f92f7c2 | ||
|
|
f3b7bca3cf | ||
|
|
9fbf5a4004 | ||
|
|
5d637360cc | ||
|
|
7d8f0c781f | ||
|
|
a212210c35 | ||
|
|
ba40925335 | ||
|
|
eab92ccb03 | ||
|
|
fc17d02451 | ||
|
|
37d514c949 | ||
|
|
a4297ef0fd | ||
|
|
c65eca58c9 | ||
|
|
18bb74e5c5 | ||
|
|
af1978fa4e | ||
|
|
5cf802eda0 | ||
|
|
80cc988f2f | ||
|
|
076795562e | ||
|
|
f5fef6eebc | ||
|
|
375ec36246 | ||
|
|
b00a17059a | ||
|
|
bcab7fef84 | ||
|
|
d2bb569ee4 | ||
|
|
52b426a343 | ||
|
|
f4fbc90217 | ||
|
|
e0ab8c581d | ||
|
|
094cdf1c7b | ||
|
|
814577fcc6 | ||
|
|
15925b66c6 | ||
|
|
925b280fd2 | ||
|
|
6b405eccd0 | ||
|
|
15326cdbad | ||
|
|
bd0b51c9ef | ||
|
|
9a4c66006e | ||
|
|
1c52780b62 | ||
|
|
bee5515e9a | ||
|
|
0a7f404d09 | ||
|
|
1dea649cff | ||
|
|
7396d8818b | ||
|
|
2d80af1dcb | ||
|
|
b5cd90a342 | ||
|
|
5358380c5f | ||
|
|
eead3a7038 | ||
|
|
ce370ec48f | ||
|
|
24ebf6bd6f | ||
|
|
b0d4386744 | ||
|
|
0c673b6a44 | ||
|
|
e89b48d23f | ||
|
|
f9ce31f98b | ||
|
|
bd131e5e4b | ||
|
|
f3132c5290 | ||
|
|
babb247e2a | ||
|
|
cc4f94f415 | ||
|
|
9885d5d9f5 | ||
|
|
8e5663a32f | ||
|
|
4bbfc3857a | ||
|
|
5404ce3c0a | ||
|
|
2289673a30 | ||
|
|
6aed6e659f | ||
|
|
42c92f80f1 | ||
|
|
fa16ee8dcb | ||
|
|
70f3e5ed05 | ||
|
|
2dc8f59bf4 | ||
|
|
d5778bad2f | ||
|
|
81c8121551 | ||
|
|
97b7555119 | ||
|
|
a5214abd04 | ||
|
|
bf8bcfafe2 | ||
|
|
8cb64698aa | ||
|
|
163d7f9791 | ||
|
|
a649a3eb50 | ||
|
|
2a8398dfb8 | ||
|
|
fd891081c4 | ||
|
|
953b7bdd59 | ||
|
|
1dde3d6e56 | ||
|
|
ec49b68b38 | ||
|
|
68590c2b27 | ||
|
|
ac4b089b18 | ||
|
|
005c196ed8 | ||
|
|
3b5803ec9a | ||
|
|
fd4a109ffa | ||
|
|
481d350bf3 | ||
|
|
d121ea84d0 | ||
|
|
a5ac41c203 | ||
|
|
e4457335b8 | ||
|
|
680f8f31ab | ||
|
|
6650eb4cd2 | ||
|
|
799fe7a5ba | ||
|
|
9a4d7b9956 | ||
|
|
fe8d2ba72f | ||
|
|
e90591bb78 | ||
|
|
e2a3565971 | ||
|
|
aeab047ede | ||
|
|
777e667f08 | ||
|
|
fa6f068fd1 | ||
|
|
439ae629e0 | ||
|
|
17fad79f71 | ||
|
|
d8531a2407 | ||
|
|
c805843e64 | ||
|
|
880f1008d6 | ||
|
|
be9cd800b0 | ||
|
|
0106cf7ae0 | ||
|
|
50303eea23 | ||
|
|
339e82d7aa | ||
|
|
5a25652c21 | ||
|
|
8f675c37fc | ||
|
|
5809b01388 | ||
|
|
f104d68569 | ||
|
|
10edba936b | ||
|
|
bbb5e99f25 | ||
|
|
ea94549cb2 | ||
|
|
9a97a80efc | ||
|
|
e4cac595c2 | ||
|
|
cf3d7def33 | ||
|
|
24bc77a935 | ||
|
|
9dd1c81d41 | ||
|
|
cddd3b552a | ||
|
|
5aa5563622 | ||
|
|
09b850cc6f | ||
|
|
273d609bac | ||
|
|
288b4564e4 | ||
|
|
78a36df901 | ||
|
|
1c1587eef6 | ||
|
|
11b810721a | ||
|
|
e5eba61cf0 | ||
|
|
54a2c13b2e | ||
|
|
b2410e428a | ||
|
|
208f24a20c | ||
|
|
8cd33a5e41 | ||
|
|
905a434179 | ||
|
|
222155d7e2 | ||
|
|
28549e35fe | ||
|
|
8f6cfa8769 | ||
|
|
f411a34af5 | ||
|
|
1de8d432d4 | ||
|
|
515a4cad73 | ||
|
|
4beacc4ca0 | ||
|
|
9c7231ee75 | ||
|
|
e31902dc4c | ||
|
|
53853a25b0 | ||
|
|
858bce7b4e | ||
|
|
77d355560f | ||
|
|
733758a909 | ||
|
|
90df2111b7 | ||
|
|
31732631fc | ||
|
|
c4d78e6422 | ||
|
|
52463fa25b | ||
|
|
96373f6959 | ||
|
|
0a67c06b51 | ||
|
|
babce70c95 | ||
|
|
4cf509989f | ||
|
|
6185bf6f61 | ||
|
|
2c31beb6e7 | ||
|
|
9f3270afaf | ||
|
|
fe2cfe2dba | ||
|
|
1ee574944c | ||
|
|
80375e68dd | ||
|
|
289d1dd7f3 | ||
|
|
f2c7e9c939 | ||
|
|
dc36dbac27 | ||
|
|
d9534325cf | ||
|
|
c221361874 | ||
|
|
70ebeda7ed | ||
|
|
11faa8149e | ||
|
|
70487a43d4 | ||
|
|
a93ae2558f | ||
|
|
c2ba9129ad | ||
|
|
c2a74b767b | ||
|
|
123c8dbd6a | ||
|
|
40ff62fa60 | ||
|
|
84397ef013 | ||
|
|
a2b7cec17b | ||
|
|
e80e7a10d4 | ||
|
|
8c4d1909f4 | ||
|
|
bb8ecd7e08 | ||
|
|
a7e1c730b2 | ||
|
|
6f1fc6c403 | ||
|
|
9f3aab6a62 | ||
|
|
44256740bd | ||
|
|
156ad0dbf5 | ||
|
|
5056d375f2 | ||
|
|
b68e801975 | ||
|
|
987f0e3016 | ||
|
|
713deba83f | ||
|
|
b9d0d80946 | ||
|
|
84cd489d24 | ||
|
|
708beb4874 | ||
|
|
4e8574bd23 | ||
|
|
fbdfba2509 | ||
|
|
e033866651 | ||
|
|
76094b8c7e | ||
|
|
e336f877c2 | ||
|
|
7f699b36ff | ||
|
|
e041b9a51c | ||
|
|
0a222a2ff7 | ||
|
|
031de097db | ||
|
|
d4361fc7d7 | ||
|
|
44617bfa9b | ||
|
|
6352106aaa | ||
|
|
7412a0e74e | ||
|
|
19d17adcfb | ||
|
|
e1e7a704e1 | ||
|
|
19dd6c391f | ||
|
|
c5cab4e719 | ||
|
|
b48f6d1405 | ||
|
|
c466f8824f | ||
|
|
2b16cd93bf | ||
|
|
0c39165701 | ||
|
|
9d1234d83a | ||
|
|
dfaa979b99 | ||
|
|
611966de63 | ||
|
|
4341df4094 | ||
|
|
83f8ec1397 | ||
|
|
fdd98879bf | ||
|
|
406f707d9a | ||
|
|
5e34b8736a | ||
|
|
7b946fa9b1 | ||
|
|
0178d1d2ee | ||
|
|
0c06f09d6d | ||
|
|
a82c6122d5 | ||
|
|
2e56f2d780 | ||
|
|
2562d637cd | ||
|
|
f69b1841e0 | ||
|
|
a3bccae173 | ||
|
|
a21144b834 | ||
|
|
7f92660598 | ||
|
|
3b60c855e1 | ||
|
|
a709041b8b | ||
|
|
8bb27ba8c4 | ||
|
|
bd4c4b5959 | ||
|
|
fb6d0f9006 | ||
|
|
6e863ecb50 | ||
|
|
764bf251cd | ||
|
|
91586bc6f6 | ||
|
|
f971fb921e | ||
|
|
c730e45f0c | ||
|
|
5c27429958 | ||
|
|
8010d6706e | ||
|
|
9c6708513a | ||
|
|
21b9b5e741 | ||
|
|
3f49b85680 | ||
|
|
65bb7a623c | ||
|
|
4293c1e138 | ||
|
|
b8e94ec066 | ||
|
|
e15872982e | ||
|
|
e4f7ddacb3 | ||
|
|
c69dede728 | ||
|
|
39370db55d | ||
|
|
73bb986737 | ||
|
|
98b9d31f24 | ||
|
|
ecf9d25ed0 | ||
|
|
668d461104 | ||
|
|
29553ba7a1 | ||
|
|
cacbd596c9 | ||
|
|
6203737093 | ||
|
|
5f642eb3e3 | ||
|
|
92a241a29e | ||
|
|
c838e1ed5b | ||
|
|
d38db89626 | ||
|
|
3f0b1e2cdc | ||
|
|
f518c6578c | ||
|
|
2dec8f449a | ||
|
|
1ef913cd36 | ||
|
|
affc6d61ca | ||
|
|
f278b27119 | ||
|
|
37bb0b89f0 | ||
|
|
02bddd251a | ||
|
|
c12fa7b4c4 | ||
|
|
b32751a693 | ||
|
|
fc1db87318 | ||
|
|
365f3fb651 | ||
|
|
a7ee681515 | ||
|
|
b02bcde8e0 | ||
|
|
bef5ccda0b | ||
|
|
a2cd2ae5a8 | ||
|
|
698e20a990 | ||
|
|
b6ad76fe1a | ||
|
|
22957a8082 | ||
|
|
8dfd5e82cb | ||
|
|
65efeec4e9 | ||
|
|
15bb334e81 | ||
|
|
77f906376e | ||
|
|
9b22f167f1 | ||
|
|
b401f42520 | ||
|
|
2949449e25 | ||
|
|
2b971400fa | ||
|
|
22df34fc70 | ||
|
|
490581a0eb | ||
|
|
0737cfb84e | ||
|
|
58895c3cf6 | ||
|
|
7fb832aa4e | ||
|
|
673c6505ae | ||
|
|
1fef434f01 | ||
|
|
495fc61a34 | ||
|
|
f80d0dab08 | ||
|
|
fb1ac8d766 | ||
|
|
bc47bd28ab | ||
|
|
5cf4771397 | ||
|
|
8891b277c6 | ||
|
|
e4abd4234d | ||
|
|
fe33fd6d86 | ||
|
|
bf75ace695 | ||
|
|
5b1b430403 | ||
|
|
424bd7e38c | ||
|
|
180ed573a5 | ||
|
|
a9b6b5b704 | ||
|
|
064d171332 | ||
|
|
30445712a5 | ||
|
|
91b6d45178 | ||
|
|
3312983b27 | ||
|
|
fecd1bde83 | ||
|
|
0176e03e36 | ||
|
|
8c35429ba3 | ||
|
|
9957cd4a2a | ||
|
|
ae7eeda88a | ||
|
|
3f4d75c8d6 | ||
|
|
1bcd361456 | ||
|
|
7962b21c00 | ||
|
|
59002d5081 | ||
|
|
ce883d2041 | ||
|
|
96847c1199 | ||
|
|
df1f12778f | ||
|
|
dff234112b | ||
|
|
e18e08ff08 | ||
|
|
f3ed562983 | ||
|
|
969fb4c981 | ||
|
|
82fcc307ef | ||
|
|
1fdab5ac0e | ||
|
|
389831218e | ||
|
|
7b5ef3ae58 | ||
|
|
9d28549f32 | ||
|
|
6cdea62638 | ||
|
|
3dd0977635 | ||
|
|
ba71ac78d9 | ||
|
|
373714c02f | ||
|
|
927b72abd2 | ||
|
|
f89e859415 | ||
|
|
dae73e21b8 | ||
|
|
f950bf4362 | ||
|
|
0621e8d1b1 | ||
|
|
5a185a0fbd | ||
|
|
45fd622491 | ||
|
|
5e7a74e2e7 | ||
|
|
94625a8fa8 | ||
|
|
dbe703cfb0 | ||
|
|
bc30899168 | ||
|
|
ce98bc9437 | ||
|
|
d39f929675 | ||
|
|
b589864144 | ||
|
|
9e0202b38f | ||
|
|
ba41685353 | ||
|
|
bb91bd2279 | ||
|
|
8b2c73de4e | ||
|
|
715d8d3881 | ||
|
|
5ad743e182 | ||
|
|
b0917d987a | ||
|
|
99d2e5233d | ||
|
|
e11e12f01d | ||
|
|
c71723101a | ||
|
|
262a641396 | ||
|
|
4d2f4ce78c | ||
|
|
51ce505d7d | ||
|
|
f85737bf29 | ||
|
|
ed4130bd2f | ||
|
|
0fbdd5e56e | ||
|
|
cc4d09423a | ||
|
|
db76671d4d | ||
|
|
05e6f06b62 | ||
|
|
f36adac8ab | ||
|
|
121268d320 | ||
|
|
e6b6543c33 | ||
|
|
e00e4a6c13 | ||
|
|
e588dfdbce | ||
|
|
2f40d19193 | ||
|
|
7a485b1a71 | ||
|
|
abc74464a0 | ||
|
|
9882dd5eff | ||
|
|
bb92a059b6 | ||
|
|
47a579f54f | ||
|
|
6746f1d13c | ||
|
|
74939c20c4 | ||
|
|
b9b31a5e72 | ||
|
|
8eab90418a | ||
|
|
83cddd8941 | ||
|
|
e8c0ba2a47 | ||
|
|
cb6adc643b | ||
|
|
0454950564 | ||
|
|
4f9bcc5ecc | ||
|
|
39e32ea7c1 | ||
|
|
2f3dcf7a16 | ||
|
|
6f0dceee09 | ||
|
|
9614c60cf6 | ||
|
|
cca758d332 | ||
|
|
52a4ca99a8 | ||
|
|
72b3f3f612 | ||
|
|
f62022cdf3 | ||
|
|
726af89168 | ||
|
|
c1c1754171 | ||
|
|
68a1ec2eb1 | ||
|
|
b85b554db1 | ||
|
|
dc04118469 | ||
|
|
7276f49f54 | ||
|
|
4704c9a0ad | ||
|
|
c15b357889 | ||
|
|
46204a9f86 | ||
|
|
5b07569eb9 | ||
|
|
b55412a0c4 | ||
|
|
81d3337681 | ||
|
|
41ee45d9f7 | ||
|
|
79e7201854 | ||
|
|
ddf899ad1e | ||
|
|
7702ed66a0 | ||
|
|
b8d7a2b8e6 | ||
|
|
8a0fb03e21 | ||
|
|
1e4f312352 | ||
|
|
77270adb96 | ||
|
|
42170e4e57 | ||
|
|
318ba1decd | ||
|
|
1fbbf0e837 | ||
|
|
6795590603 | ||
|
|
196120fabd | ||
|
|
dd3725a452 | ||
|
|
c8cd67c989 | ||
|
|
aea87a9a2f | ||
|
|
aa0e41c389 | ||
|
|
c4a1cad81b | ||
|
|
0f3fbb267b | ||
|
|
a17b3fc838 | ||
|
|
dd8feb49aa | ||
|
|
b572028e95 | ||
|
|
a01a1b4556 | ||
|
|
3121b34d17 | ||
|
|
36305e5bd8 | ||
|
|
541e3b6eb4 | ||
|
|
a24f4421d6 | ||
|
|
e8399416ae | ||
|
|
d0fa8acf86 | ||
|
|
ced7bef274 | ||
|
|
311644f39b | ||
|
|
91e40e668c | ||
|
|
987f6237c4 | ||
|
|
b9a8310f4b | ||
|
|
ddba4b90c3 | ||
|
|
98f98048bc | ||
|
|
367ac04ed8 | ||
|
|
43b18b20cc | ||
|
|
06d8dba4a0 | ||
|
|
b79c3aadd2 | ||
|
|
46985bf750 | ||
|
|
36b3878a45 | ||
|
|
bc8120c864 | ||
|
|
8b6d9d8216 | ||
|
|
413bf85dde | ||
|
|
97ef85c9aa | ||
|
|
f649172580 | ||
|
|
aad76acb68 | ||
|
|
5eb1c411a5 | ||
|
|
0d6001a196 | ||
|
|
0ab21da820 | ||
|
|
204bd29e52 | ||
|
|
fd8b37dc14 | ||
|
|
94550682d7 | ||
|
|
7754f96549 | ||
|
|
ee2d01ed88 | ||
|
|
e97f9fca22 | ||
|
|
177c06b1f1 | ||
|
|
cc348dcca2 | ||
|
|
8622af77c1 | ||
|
|
3b21f469ca | ||
|
|
82033d5029 | ||
|
|
da5ccbf7d1 | ||
|
|
a22a92b481 | ||
|
|
5bd262f9e9 | ||
|
|
2b93ef1d75 | ||
|
|
05abfc8ded | ||
|
|
de8e810792 | ||
|
|
cb28380f47 | ||
|
|
d104335501 | ||
|
|
7263cc675b | ||
|
|
d850efdd08 | ||
|
|
a6bd0944db | ||
|
|
7df2dd67bd | ||
|
|
5be95aa377 | ||
|
|
c2f978bd1e | ||
|
|
21815a1427 | ||
|
|
877fcb35f4 | ||
|
|
c2f7e166a0 | ||
|
|
e171b4e3f0 | ||
|
|
2a6f31a84e | ||
|
|
bc4473b275 | ||
|
|
43c38a5aeb | ||
|
|
6e5ff1dbe8 | ||
|
|
4edade8079 | ||
|
|
9352988c44 | ||
|
|
e5e0a21fc6 | ||
|
|
fb456e00ac | ||
|
|
851e03966d | ||
|
|
38e3299675 | ||
|
|
dbdeca3032 | ||
|
|
a78e4c6cf3 | ||
|
|
76e785dd5e | ||
|
|
767d0d9046 | ||
|
|
145efb1a33 | ||
|
|
22d2af2a29 | ||
|
|
13801ebd74 | ||
|
|
614fa7b853 | ||
|
|
4b815eece9 | ||
|
|
11f350e63f | ||
|
|
4dc3c7a6a0 | ||
|
|
d106053e4c | ||
|
|
5315751dc9 | ||
|
|
e448a9a4a9 | ||
|
|
5b0d9338fd | ||
|
|
409d7a99f9 | ||
|
|
f77e581468 | ||
|
|
d3217718a9 | ||
|
|
7e76480db4 | ||
|
|
7856141fc4 | ||
|
|
232cf8c28b | ||
|
|
b51805b87e | ||
|
|
392ef77bc5 | ||
|
|
de83c3e9e0 | ||
|
|
050d233e10 | ||
|
|
8c1f834a4a | ||
|
|
cbeb5ab960 | ||
|
|
33a51a1bdc | ||
|
|
73660f779b | ||
|
|
2c79332de7 | ||
|
|
2aec025e16 | ||
|
|
24f486261a | ||
|
|
f0c4944e2c | ||
|
|
cf15872b8d | ||
|
|
78e1b9b7c1 | ||
|
|
967a8d0678 | ||
|
|
fa44cef27b | ||
|
|
29ba3df717 | ||
|
|
aacc222b7d | ||
|
|
341f0bf9e3 | ||
|
|
091c6a3bda | ||
|
|
fd53cad208 | ||
|
|
1b6d6d0dc0 | ||
|
|
40c671b1a5 | ||
|
|
0a8b1c199d | ||
|
|
3a3c823ddb | ||
|
|
900f3c9a09 | ||
|
|
8fe2f747f4 | ||
|
|
adaeff5540 | ||
|
|
d0f91c00cb | ||
|
|
e20a071977 | ||
|
|
737a6921c3 | ||
|
|
44af6978db | ||
|
|
acbdf63a48 | ||
|
|
e59beeb625 | ||
|
|
f01dc8cc34 | ||
|
|
89f0e45b50 | ||
|
|
3f83556128 | ||
|
|
1cdafabc7c | ||
|
|
f9965b4967 | ||
|
|
0de0942f9c | ||
|
|
00a46f104a | ||
|
|
009755ba01 | ||
|
|
53a96f69ef | ||
|
|
e60bc69515 | ||
|
|
fbadb8354f | ||
|
|
02dee05c16 | ||
|
|
b16316ae37 | ||
|
|
f5077dc982 | ||
|
|
634c438b50 | ||
|
|
f468c492b9 | ||
|
|
5410838793 | ||
|
|
91c2618f21 | ||
|
|
beabd3c6b7 | ||
|
|
a81c7bf04b | ||
|
|
a6c3bb4c6d | ||
|
|
902d190a11 | ||
|
|
0142f113a3 | ||
|
|
fe195a86cd | ||
|
|
11845cedd6 | ||
|
|
5e43e2d333 | ||
|
|
15cc47334e | ||
|
|
5989c1883e | ||
|
|
632de523de | ||
|
|
2db7513590 | ||
|
|
ced24f9215 | ||
|
|
b302cad46d | ||
|
|
33838b8c1c | ||
|
|
42fb74ce22 | ||
|
|
74670ec07f | ||
|
|
3450a78992 | ||
|
|
f4ef6180b8 | ||
|
|
f0c25aaa60 | ||
|
|
0f63c1ef8f | ||
|
|
b27966860e | ||
|
|
51566d43bd | ||
|
|
71d085bd13 | ||
|
|
ffb10e7aac | ||
|
|
a5503df32b | ||
|
|
c8db96705f | ||
|
|
f94efab6c3 | ||
|
|
4c0af0f79e | ||
|
|
fc7e327d19 | ||
|
|
045f44c7f9 | ||
|
|
867d13b007 | ||
|
|
40991b93a2 | ||
|
|
949d76b328 | ||
|
|
210a2ccfdf | ||
|
|
3862aad2f7 | ||
|
|
2a005c2f11 | ||
|
|
ab46ac9bd8 | ||
|
|
c51ed84fd8 | ||
|
|
74054ea907 | ||
|
|
f79bc03360 | ||
|
|
b553df860c | ||
|
|
f753a728dd | ||
|
|
3f2e8ebcdf | ||
|
|
a1fe83c070 | ||
|
|
879c94b01e | ||
|
|
da40445d75 | ||
|
|
6235e1ae57 | ||
|
|
3618432107 | ||
|
|
f1cba8a0ea | ||
|
|
489760d2e3 | ||
|
|
caf9fc5687 | ||
|
|
dc93c2afec | ||
|
|
9164c4f309 | ||
|
|
e241f880e8 | ||
|
|
662f1355de | ||
|
|
c865211c2f | ||
|
|
6324068d34 | ||
|
|
e77e18f8c4 | ||
|
|
69c337b31d | ||
|
|
bc99f75b53 | ||
|
|
421582d747 | ||
|
|
ab1606a0f6 | ||
|
|
bcfaeca373 | ||
|
|
5f0e261f2d | ||
|
|
0e1aec4348 | ||
|
|
c1c65777b6 | ||
|
|
89d666155d | ||
|
|
4d6d65ee33 | ||
|
|
dcdb360a19 | ||
|
|
8a73990f51 | ||
|
|
efe229a54b | ||
|
|
d6fdf7d4b1 | ||
|
|
cf180b651b | ||
|
|
2e649e1843 | ||
|
|
1985579148 | ||
|
|
d31dbc6798 | ||
|
|
ffd45a2261 | ||
|
|
135631d29d | ||
|
|
ce7e31cd04 | ||
|
|
4ff1eb1890 | ||
|
|
7c47c917b5 | ||
|
|
7285459375 | ||
|
|
52b7ebd4e7 | ||
|
|
ea6f624b58 | ||
|
|
3cf3b78192 | ||
|
|
cdc2d034de | ||
|
|
cd8a17d644 | ||
|
|
57e3e7f150 | ||
|
|
c759639603 | ||
|
|
a6c101aeb8 | ||
|
|
7ea8a754c1 | ||
|
|
d5667343cb | ||
|
|
0bfe435c4f | ||
|
|
cd39843c75 | ||
|
|
f9ae65acd8 | ||
|
|
cfd930cae7 | ||
|
|
0b7e3610ac | ||
|
|
10946e7669 | ||
|
|
2dcd8c2729 | ||
|
|
dcde1c2a8f | ||
|
|
0810384555 | ||
|
|
434fb1e0da | ||
|
|
435a0b98ac | ||
|
|
05eda236cc | ||
|
|
c998faeeab | ||
|
|
f967933e8b | ||
|
|
8862a1ac8f | ||
|
|
ad56a115be | ||
|
|
c8ed013600 | ||
|
|
c49fc8db72 | ||
|
|
93fa268db1 | ||
|
|
521c92764d | ||
|
|
71d121fab2 | ||
|
|
86970a01d7 | ||
|
|
210496f0ca | ||
|
|
2eb0386c2a | ||
|
|
440c99100b | ||
|
|
33d5e646e5 | ||
|
|
c00a62d37f | ||
|
|
17486408b4 | ||
|
|
c17594c1d0 | ||
|
|
4463239d6e | ||
|
|
45d82c8d99 | ||
|
|
3e302e8683 | ||
|
|
d6620f0f3a | ||
|
|
f7a5b71dba | ||
|
|
a4d14fc987 | ||
|
|
c8587e1242 | ||
|
|
4e62a88870 | ||
|
|
32f6e7e231 | ||
|
|
94f7bcef67 | ||
|
|
36e90a1bd5 | ||
|
|
9fbe900949 | ||
|
|
73f1511ac2 | ||
|
|
5937932370 | ||
|
|
794c93b540 | ||
|
|
b92ac33022 | ||
|
|
90bbcbf994 | ||
|
|
151995f88c | ||
|
|
611b3c4c2c | ||
|
|
2a6104ab17 | ||
|
|
1dcd4cc220 | ||
|
|
7174578ac3 | ||
|
|
10d09049bb | ||
|
|
98c6a4e86e | ||
|
|
3bd52891c5 | ||
|
|
ee752ab1ce | ||
|
|
9eb900c448 | ||
|
|
0e919d25e9 | ||
|
|
4ed34d2c6f | ||
|
|
c893b44da2 | ||
|
|
d06a736f41 | ||
|
|
b135a0aa49 | ||
|
|
c73daef2a1 | ||
|
|
bc08c1d2b7 | ||
|
|
8451feaa7a | ||
|
|
e30823769c | ||
|
|
12e3b52f4d | ||
|
|
5812b22662 | ||
|
|
f139b14a3c | ||
|
|
291f1382c5 | ||
|
|
e0fe83740a | ||
|
|
9012ec5310 | ||
|
|
c37ecb1552 | ||
|
|
59d8e76081 | ||
|
|
5097ac05bf | ||
|
|
a9c154691b | ||
|
|
d628a9bc78 | ||
|
|
2fb1859732 | ||
|
|
5f5d16c698 | ||
|
|
f666e772a2 | ||
|
|
8974b12af5 | ||
|
|
f96d5c9633 | ||
|
|
50336aaedd | ||
|
|
6a04357547 | ||
|
|
0da64f430e | ||
|
|
e27ab3a24f | ||
|
|
121de4ef47 | ||
|
|
77f7d99048 | ||
|
|
4b035b9902 | ||
|
|
9778b1411c | ||
|
|
636ff411f1 | ||
|
|
02598d1e91 | ||
|
|
31aea3c996 | ||
|
|
a60f2588d6 | ||
|
|
d518f5f307 | ||
|
|
d5cbacba50 | ||
|
|
ab0f3da650 | ||
|
|
52de24b95b | ||
|
|
70b60bbe16 | ||
|
|
aff890bce8 | ||
|
|
905f1609b3 | ||
|
|
c978b25f7f | ||
|
|
2b526756f0 | ||
|
|
8a436d5103 | ||
|
|
1dd050aaca | ||
|
|
99bcce7892 | ||
|
|
0e2ea94f5e | ||
|
|
9cd7abdd6e | ||
|
|
dbacfe0a1d | ||
|
|
d50494f274 | ||
|
|
4b5e867a69 | ||
|
|
969306f0f1 | ||
|
|
af722a2978 | ||
|
|
7fce9346dc | ||
|
|
b23a6172ed | ||
|
|
ea58a8103c | ||
|
|
7857c22277 | ||
|
|
b3c7e948be | ||
|
|
c966a538fb | ||
|
|
3027a7d7e2 | ||
|
|
dd22176935 | ||
|
|
dbf9d0f094 | ||
|
|
b3218d45f3 | ||
|
|
3ca5184998 | ||
|
|
007ab69777 | ||
|
|
12d374d4b7 | ||
|
|
0ddd7de1ba | ||
|
|
f385193a89 | ||
|
|
ff078920bc | ||
|
|
b62f443be5 | ||
|
|
553c4203ab | ||
|
|
df16a7c4f3 | ||
|
|
896535fe87 | ||
|
|
89434f6a2f | ||
|
|
4e520cdd30 | ||
|
|
72dcdb7655 | ||
|
|
db5510b866 | ||
|
|
61e0efa0c7 | ||
|
|
12514c09b2 | ||
|
|
90a0590de1 | ||
|
|
b09c891290 | ||
|
|
d28bb04a9c | ||
|
|
9de1c71ff7 | ||
|
|
3ce6dd166c | ||
|
|
684bef066f | ||
|
|
52f2221be9 | ||
|
|
1bee131d52 | ||
|
|
2731976a95 | ||
|
|
f7d5e81130 | ||
|
|
371bc91d73 | ||
|
|
89771de6d0 | ||
|
|
71b213df63 | ||
|
|
7f3913ff58 | ||
|
|
d884034c9f | ||
|
|
bec199c4a2 | ||
|
|
3686110ed2 | ||
|
|
708e421ec3 | ||
|
|
5ae7ee5276 | ||
|
|
65493b421a | ||
|
|
301511a840 | ||
|
|
e86a0a4325 | ||
|
|
8d05279599 | ||
|
|
587e0e3e0c | ||
|
|
2315f32ca0 | ||
|
|
5a71509353 | ||
|
|
a310bc19a7 | ||
|
|
0647533f8c | ||
|
|
99dd78964a | ||
|
|
0d1606ff23 | ||
|
|
a6a92d42f9 | ||
|
|
2aabe16163 | ||
|
|
5984c996a8 | ||
|
|
c8d126f6ef | ||
|
|
9004554da3 | ||
|
|
4eb9efdd33 | ||
|
|
83740d81f2 | ||
|
|
c8581e4cd9 | ||
|
|
612e33c499 | ||
|
|
3e8ac30940 | ||
|
|
4bcddd72e6 | ||
|
|
00bb70a62d | ||
|
|
b4990b5bb4 | ||
|
|
65095253a9 | ||
|
|
c87efb77bc | ||
|
|
03159254ce | ||
|
|
ec5cf8593e | ||
|
|
9f065c0ea5 | ||
|
|
df3f05e824 | ||
|
|
9a10afbbc6 | ||
|
|
8d916b8573 | ||
|
|
653657ae43 | ||
|
|
b0b7240022 | ||
|
|
1e82003bf9 | ||
|
|
4be7fccad3 | ||
|
|
b040ace4c2 | ||
|
|
ab572ce5b9 | ||
|
|
ded77f59cd | ||
|
|
a1c5415f8f | ||
|
|
00e8988b52 | ||
|
|
5680808fb6 | ||
|
|
814bcc04ad | ||
|
|
a8cbff08ef | ||
|
|
ee8a05c3c7 | ||
|
|
46d7f6f95d | ||
|
|
30c89310a6 | ||
|
|
d6e1b86147 | ||
|
|
a8e973f5d7 | ||
|
|
c4d1354c54 | ||
|
|
dc2e700057 | ||
|
|
bfe0c3a8e0 | ||
|
|
a387270b76 | ||
|
|
59b659b027 | ||
|
|
973472c51e | ||
|
|
9052511c86 | ||
|
|
f57d4818a4 | ||
|
|
c5973dd411 | ||
|
|
e5f38bf391 | ||
|
|
6b4db944dd | ||
|
|
0a761d238a | ||
|
|
c8f026c382 | ||
|
|
2c70c12814 | ||
|
|
b31f6eb45f | ||
|
|
d387c1c83a | ||
|
|
7a1a2b9aa6 | ||
|
|
444da3ecec | ||
|
|
1918914598 | ||
|
|
29f3f2e1d8 | ||
|
|
759d11ce1a | ||
|
|
729e24da78 | ||
|
|
ec362536f5 | ||
|
|
b92fe3eecb | ||
|
|
985de035bc | ||
|
|
2e40ceaaa4 | ||
|
|
02a797f9f9 | ||
|
|
b938154f6b | ||
|
|
e312ebcb57 | ||
|
|
02cf50063e | ||
|
|
f511bec26c | ||
|
|
9f93c16bbf | ||
|
|
892e75ca92 | ||
|
|
5e73068e09 | ||
|
|
b71d9ce3f6 | ||
|
|
730529353d | ||
|
|
101f57d5e0 | ||
|
|
2cfc9e286e | ||
|
|
998ed99288 | ||
|
|
9048a7f021 | ||
|
|
1c91bb5a20 | ||
|
|
b957a81ea5 | ||
|
|
19fdeae4ba | ||
|
|
50ecd2c12c | ||
|
|
07f9212799 | ||
|
|
8a8cba75a2 | ||
|
|
1ae3808e13 | ||
|
|
ee2936ffbf | ||
|
|
5b29f1cdec | ||
|
|
bce29ac353 | ||
|
|
f7584fa883 | ||
|
|
18074ff98f | ||
|
|
4ad60b477c | ||
|
|
69469496e2 | ||
|
|
baaa2592a6 | ||
|
|
ecb8aa6fc1 | ||
|
|
34210901bf | ||
|
|
7132893863 | ||
|
|
cfe04d4453 | ||
|
|
f98f2890f3 | ||
|
|
d93c21349f | ||
|
|
9358dbbd99 | ||
|
|
2b9d304816 | ||
|
|
e6465a6f44 | ||
|
|
4aa9244b92 | ||
|
|
1ac4d1b793 | ||
|
|
ea205f2fcd | ||
|
|
9c15ffbb74 | ||
|
|
09b4485870 | ||
|
|
19282d6d6c | ||
|
|
ee85aaccd2 | ||
|
|
0afc34f6fc | ||
|
|
45bfccfddc | ||
|
|
f8501478f6 | ||
|
|
a5582e0960 | ||
|
|
b4fe72c4a2 | ||
|
|
48f7678771 | ||
|
|
808098413c | ||
|
|
74fd2dc9ad | ||
|
|
443b2551c7 | ||
|
|
7b681bc94a | ||
|
|
bd29f7168c | ||
|
|
c8c0dc2f53 | ||
|
|
17c6be6a9b | ||
|
|
318e752fd6 | ||
|
|
3dcfd7ba41 | ||
|
|
604a92dc3d | ||
|
|
b146ae96a7 | ||
|
|
0963e91c34 | ||
|
|
5d13b5a2fb | ||
|
|
90db821036 | ||
|
|
99d1fdd270 | ||
|
|
10ab72855d | ||
|
|
0d18516af8 | ||
|
|
e22c815444 | ||
|
|
e579e98469 | ||
|
|
723eaa1f40 | ||
|
|
4ebeeaf1e0 | ||
|
|
2c9ceea3cf | ||
|
|
c289872ab7 | ||
|
|
9469bdc93e | ||
|
|
9b4564d7f3 | ||
|
|
b5b5844119 | ||
|
|
6ef7563f53 | ||
|
|
e3e3200f95 | ||
|
|
7a0f814043 | ||
|
|
80b62df0a9 | ||
|
|
81d4e201bb | ||
|
|
8155b8cfb3 | ||
|
|
276e35ecfb | ||
|
|
7d8657287e | ||
|
|
f7905582db | ||
|
|
f12e6837be | ||
|
|
3870031c6e | ||
|
|
060a1fc727 | ||
|
|
28783abbe2 | ||
|
|
974a853f40 | ||
|
|
7ff9825f55 | ||
|
|
04b4971d8c | ||
|
|
6c3f6cfd89 | ||
|
|
ee833ea594 | ||
|
|
338feef70e | ||
|
|
ec68aaf43b | ||
|
|
24df78bcbc | ||
|
|
e926a93568 | ||
|
|
28476e3b7b | ||
|
|
05aa0ad114 | ||
|
|
5ceaed291f | ||
|
|
48934414b2 | ||
|
|
c1804810d8 | ||
|
|
69ee35c909 | ||
|
|
5d2e93e5f0 | ||
|
|
1cbd2121e2 | ||
|
|
c0d9e3231b | ||
|
|
2775fb5a45 | ||
|
|
6ca8bc4037 | ||
|
|
5dd6b1effc | ||
|
|
369e9fbf08 | ||
|
|
e39d012312 | ||
|
|
7d077a2260 | ||
|
|
b30faa577d | ||
|
|
91356d2d03 | ||
|
|
61f0231ce0 | ||
|
|
482a18e6de | ||
|
|
382ddac1a1 | ||
|
|
2b3e020152 | ||
|
|
01391f0234 | ||
|
|
32c6f96000 | ||
|
|
a883fe7b8e | ||
|
|
5d74d97fe1 | ||
|
|
db4844e3f0 | ||
|
|
7ca4fd817e | ||
|
|
8e13bcd43d | ||
|
|
cacdbb5262 | ||
|
|
35729f0870 | ||
|
|
0ff98eb2f5 | ||
|
|
dbe33783c9 | ||
|
|
f38352f1af | ||
|
|
1bf2e313f6 | ||
|
|
ca26fd17a7 | ||
|
|
4085a3fad4 | ||
|
|
36f470383b | ||
|
|
52dc9ef418 | ||
|
|
bada7e0f92 | ||
|
|
ec93b03d8d | ||
|
|
a5e748de84 | ||
|
|
2809405164 | ||
|
|
79ecf2edb7 | ||
|
|
c2ebf5544b | ||
|
|
d6fbe2a5ad | ||
|
|
b89e10c3e6 | ||
|
|
5a6c64cc28 | ||
|
|
aecbce283d | ||
|
|
60f920df0e | ||
|
|
da19974e81 | ||
|
|
ab9feb2e35 | ||
|
|
b8d798158b | ||
|
|
82d7c78b0c | ||
|
|
0074b7becb | ||
|
|
33237419c1 | ||
|
|
aa6f6bc3aa | ||
|
|
f1e023af85 | ||
|
|
f71a590375 | ||
|
|
9f48488d87 | ||
|
|
085c1594de | ||
|
|
56c6301089 | ||
|
|
e303f30d10 | ||
|
|
4b9883a573 | ||
|
|
9fb46e1206 | ||
|
|
8216303524 | ||
|
|
6c7357bbf9 | ||
|
|
13add693db | ||
|
|
a491aa0f6a | ||
|
|
e8f49f1b95 | ||
|
|
0a8c53575c | ||
|
|
3c40d93f36 | ||
|
|
4335e99f2e | ||
|
|
1fd4b851f5 | ||
|
|
142cc8fe2a | ||
|
|
5c37c63cef | ||
|
|
39caf630a9 | ||
|
|
b0502d1f3b | ||
|
|
7c9b545b30 | ||
|
|
678dcc2ca5 | ||
|
|
54d4d9a881 | ||
|
|
a5e06ed5b7 | ||
|
|
63038e0305 | ||
|
|
7cea4d7df6 | ||
|
|
936066aa03 | ||
|
|
e7f3224091 | ||
|
|
974f6982a1 | ||
|
|
7fe7e1e984 | ||
|
|
9148ab79f3 | ||
|
|
1204f15bfa | ||
|
|
f1c7e73bef | ||
|
|
c18abd4fbe | ||
|
|
c3ac875055 | ||
|
|
dbd125c714 | ||
|
|
695e001984 | ||
|
|
33b42872e5 | ||
|
|
8bb7602145 | ||
|
|
d9e2edd1df | ||
|
|
f9d80d5bd2 | ||
|
|
406874b6c2 | ||
|
|
e4ddd6cb6d | ||
|
|
a7745e9a86 | ||
|
|
3fd7bc8058 | ||
|
|
25e398a8f0 | ||
|
|
c31b03327f | ||
|
|
f6d56e2937 | ||
|
|
ca7769e041 | ||
|
|
c0aef7dd6b | ||
|
|
4283ecf774 | ||
|
|
88a57dc98d | ||
|
|
035ecef8a5 | ||
|
|
4c3b6814dc | ||
|
|
3f016fcc5a | ||
|
|
be75c483d0 | ||
|
|
08dd32d32d | ||
|
|
b49584457a | ||
|
|
40d25adb88 | ||
|
|
a38e5a44ec | ||
|
|
3fceef0936 | ||
|
|
c065427ebe | ||
|
|
c82f04f54c | ||
|
|
d9d0c52770 | ||
|
|
c60635855d | ||
|
|
2666d6df90 | ||
|
|
8558fa0bcf | ||
|
|
f36d6b7ef5 | ||
|
|
e9908048ec | ||
|
|
dceed2ea5c | ||
|
|
7b63878926 | ||
|
|
01ecd3517e | ||
|
|
fbdea8dc71 | ||
|
|
58d0e8b28f | ||
|
|
07fbe82305 | ||
|
|
05a967564a | ||
|
|
75ef1278e6 | ||
|
|
fc158ad5c0 | ||
|
|
4d69d0eda5 | ||
|
|
d7ba4bbb9b | ||
|
|
2225b8add7 | ||
|
|
9572db262c | ||
|
|
515e4b9187 | ||
|
|
c1dc1a20d6 | ||
|
|
a58083b11e | ||
|
|
0f24fba28a | ||
|
|
4d774e275d | ||
|
|
a2d6a50dff | ||
|
|
08b56e8180 | ||
|
|
7a77917df6 | ||
|
|
e432014a88 | ||
|
|
a89db9872d | ||
|
|
6dcc85dcf4 | ||
|
|
a375ea4ee2 | ||
|
|
d74464e091 | ||
|
|
e2c2afbbe0 | ||
|
|
cffe54d094 | ||
|
|
2f02120fee | ||
|
|
c0b534e531 | ||
|
|
5eaef50a77 | ||
|
|
a80c4d147c | ||
|
|
ad66b2bf13 | ||
|
|
ef6ec9de01 | ||
|
|
751deac9d1 | ||
|
|
406da499ff | ||
|
|
a6b48d5273 | ||
|
|
6272b5c58c | ||
|
|
8c9b99458d | ||
|
|
3273c68b63 | ||
|
|
a44e2190d6 | ||
|
|
2dab478c3f | ||
|
|
a32afb28c0 | ||
|
|
d2a89b424b | ||
|
|
9502cd7047 | ||
|
|
cd02450095 | ||
|
|
03a3412e62 | ||
|
|
33086e6db0 | ||
|
|
d7a951996b | ||
|
|
38bfe50508 | ||
|
|
a70567e74f | ||
|
|
db472c73c1 | ||
|
|
29824f9baa | ||
|
|
32b1a9fc8a | ||
|
|
018719d9d3 | ||
|
|
8ae472821f | ||
|
|
26911ff21c | ||
|
|
5070cc07be | ||
|
|
1e65eb4936 | ||
|
|
13e40f754c | ||
|
|
87cfd930e4 | ||
|
|
8b63a89267 | ||
|
|
94eff945b4 | ||
|
|
039737ffc2 | ||
|
|
08370c7230 | ||
|
|
2f9b28c0fc | ||
|
|
4e1320d480 | ||
|
|
e6202cfa97 | ||
|
|
a15b28a24e | ||
|
|
9975dbeded | ||
|
|
2cf5eec62f | ||
|
|
51012d07cc | ||
|
|
252abbeb8d | ||
|
|
357b28f675 | ||
|
|
bf37afd7c7 | ||
|
|
f311045320 | ||
|
|
381a7b110a | ||
|
|
75f2c56558 | ||
|
|
91333c5d2f | ||
|
|
ee2ba4e52f | ||
|
|
ae300c98a4 | ||
|
|
748b70311e | ||
|
|
8d0664186a | ||
|
|
2fb5a71efd | ||
|
|
4fda366b67 | ||
|
|
2ceae6014e | ||
|
|
cf3162cff1 | ||
|
|
2350d941a5 | ||
|
|
25f9f55878 | ||
|
|
07540f3386 | ||
|
|
fe63f36656 | ||
|
|
434daf87c2 | ||
|
|
c681e5e082 | ||
|
|
549f97b54a | ||
|
|
f47a93b0c1 | ||
|
|
3fe5a99dab | ||
|
|
097bc4c622 | ||
|
|
252126cf76 | ||
|
|
8609d1e790 | ||
|
|
9809f1ff66 | ||
|
|
796098e4c9 | ||
|
|
eb6bcdb3ee | ||
|
|
a1555e0615 | ||
|
|
44b527415f | ||
|
|
ed294c5945 | ||
|
|
bd3ade31fa | ||
|
|
d03d2e6d3d | ||
|
|
58ffa8d526 | ||
|
|
bfe8c15a29 | ||
|
|
2fa7e003b1 | ||
|
|
ba948669cd | ||
|
|
666e84eef2 | ||
|
|
69b15c889d | ||
|
|
ff3bf6d7f5 | ||
|
|
b844129649 | ||
|
|
86059bd5dd | ||
|
|
9927d991ef | ||
|
|
6849b7d555 | ||
|
|
1efd1ee6ac | ||
|
|
d12600fba0 | ||
|
|
769b2a3e30 | ||
|
|
d237ec7e78 | ||
|
|
de1375201f | ||
|
|
13ba8810c7 | ||
|
|
bdfbac2aa6 | ||
|
|
6bd0bd2ed1 | ||
|
|
211a889528 | ||
|
|
3bb29bf63a | ||
|
|
78d52ad2de | ||
|
|
4fe648415f | ||
|
|
6751ac4970 | ||
|
|
6764957d06 | ||
|
|
c719ba7bf3 | ||
|
|
31d7293b17 | ||
|
|
defef2222a | ||
|
|
63459f65fb | ||
|
|
c68e1ceb64 | ||
|
|
07e72d2ac8 | ||
|
|
7f97e354b4 | ||
|
|
e5a554f0b5 | ||
|
|
9e4aa79278 | ||
|
|
a0298728df | ||
|
|
0eb83f6ee6 | ||
|
|
e81f092520 | ||
|
|
7fb874ad28 | ||
|
|
a4a41b9023 | ||
|
|
99aea7eb6f | ||
|
|
b4ac36337f | ||
|
|
7072bc3203 | ||
|
|
7e9f000d0b | ||
|
|
af56eebc6a | ||
|
|
387c778496 | ||
|
|
fbbde125d9 | ||
|
|
dbaefd6126 | ||
|
|
a1c87f7c49 | ||
|
|
045ee53468 | ||
|
|
b84ab718b0 | ||
|
|
6144deb6b6 | ||
|
|
19864a4a08 | ||
|
|
7920b6e163 | ||
|
|
4928166593 | ||
|
|
ad5f520c68 | ||
|
|
150b4fb4f6 | ||
|
|
4f8198b298 | ||
|
|
076f177100 | ||
|
|
3b939480a4 | ||
|
|
773a2767c7 | ||
|
|
e9b29184d0 | ||
|
|
0d02b8d1f6 | ||
|
|
98c0fcc659 | ||
|
|
1ddebcc862 | ||
|
|
1adb900561 | ||
|
|
c5c1012ddd | ||
|
|
519f81771a | ||
|
|
c762c06b17 | ||
|
|
8bab543531 | ||
|
|
bab8c82a35 | ||
|
|
b82bca7c99 | ||
|
|
556a2126b5 | ||
|
|
a070751061 | ||
|
|
847845027f | ||
|
|
c69914367d | ||
|
|
48d364e269 | ||
|
|
c58c27ed8b | ||
|
|
97b8abf16c | ||
|
|
59567b43b0 | ||
|
|
e067e84d69 | ||
|
|
35c670513f | ||
|
|
8ec92b6fe7 | ||
|
|
5552f082e8 | ||
|
|
3c122117fc | ||
|
|
a467d21eba | ||
|
|
bc724ea206 | ||
|
|
4ea5c1d973 | ||
|
|
c65883a727 | ||
|
|
a4f6146667 | ||
|
|
936c6c0d5c | ||
|
|
91af8878e1 | ||
|
|
d2b2ae09e6 | ||
|
|
94c35184a9 | ||
|
|
9e021cd1a8 | ||
|
|
78565026ac | ||
|
|
de6be743c6 | ||
|
|
f534e80c71 | ||
|
|
0797ab6bc6 | ||
|
|
b4df26a75d | ||
|
|
d92c462466 | ||
|
|
f31b905847 | ||
|
|
572e457e77 | ||
|
|
be9b23a082 | ||
|
|
c9e7531251 | ||
|
|
90faecd86e | ||
|
|
6c12e5bfbe | ||
|
|
2e069429c2 | ||
|
|
e65202f825 | ||
|
|
11a3057e8e | ||
|
|
ba0c619844 | ||
|
|
a83fb30836 | ||
|
|
dc092653f4 | ||
|
|
25c6278000 | ||
|
|
5033e0e752 | ||
|
|
295eeef9b6 | ||
|
|
f63b87e28b | ||
|
|
1f780e86d5 | ||
|
|
408a56ffaf | ||
|
|
a84f5d52ff | ||
|
|
fb6eed0087 | ||
|
|
dc242a01b4 | ||
|
|
074d3dd2b1 | ||
|
|
807afbc879 | ||
|
|
8f1eeb1025 | ||
|
|
dd288f70ec | ||
|
|
6a11134a18 | ||
|
|
9b60b55f03 | ||
|
|
77aa06df64 | ||
|
|
9b7860d6ec | ||
|
|
bda73b1bc9 | ||
|
|
8576ee0981 | ||
|
|
8093b55e86 | ||
|
|
b6db4a0e2e | ||
|
|
cd7894dfc9 | ||
|
|
c36d73f42a | ||
|
|
9871c4ff06 | ||
|
|
e330983408 | ||
|
|
4bbf443ba2 | ||
|
|
0234c62c3e | ||
|
|
0bdc27186d | ||
|
|
c51c55b782 | ||
|
|
26250f1953 | ||
|
|
7ecfe6304a | ||
|
|
b7be03801c | ||
|
|
b21cb8a396 | ||
|
|
eadd56a376 | ||
|
|
2aa9a66c57 | ||
|
|
8e9c07df39 | ||
|
|
1d392f0e76 | ||
|
|
08cbc1b22b | ||
|
|
f744d7a789 | ||
|
|
ff2a737094 | ||
|
|
111a1cef52 | ||
|
|
a44b56dc61 | ||
|
|
1b586b69bc | ||
|
|
4a7f2ffc5e | ||
|
|
503162ec54 | ||
|
|
22fad86453 | ||
|
|
8641b8a36e | ||
|
|
be2d3b5886 | ||
|
|
3120487243 | ||
|
|
4404ef0281 | ||
|
|
0c68fc409e | ||
|
|
ea7bf19057 | ||
|
|
99bf9775d9 | ||
|
|
0e2d5f9d0e | ||
|
|
3065b9f968 | ||
|
|
cce5fbc7b1 | ||
|
|
82a4d4ec65 | ||
|
|
708147625f | ||
|
|
93559a54e9 | ||
|
|
f10ddea305 | ||
|
|
4a8ff6d824 | ||
|
|
4b7e3af77c | ||
|
|
19238a910c | ||
|
|
2deb13df43 | ||
|
|
1ece33e79a | ||
|
|
844f5e5af1 | ||
|
|
51bfac71b0 | ||
|
|
81a0fd782f | ||
|
|
3a30e5bf78 | ||
|
|
a87096cdc9 | ||
|
|
527d99fd39 | ||
|
|
6e2722a6d3 | ||
|
|
8694deaabb | ||
|
|
a226099b04 | ||
|
|
336ba8f419 | ||
|
|
5abdc99f3e | ||
|
|
f7ef9887d7 | ||
|
|
fed76f29d0 | ||
|
|
a3cfbfc6ba | ||
|
|
444646b65e | ||
|
|
8bd508a73d | ||
|
|
1cceb4e0ad | ||
|
|
c2d2034ae8 | ||
|
|
f94ed922e5 | ||
|
|
7a57d28db4 | ||
|
|
ce5121e659 | ||
|
|
a7d7158b05 | ||
|
|
6b8c78bacc | ||
|
|
2da930ba4c | ||
|
|
772cac7946 | ||
|
|
42f672d85d | ||
|
|
945f2912a7 | ||
|
|
1ccf516c5e | ||
|
|
fb396b8fac | ||
|
|
6db3026d47 | ||
|
|
becc306300 | ||
|
|
4e7ce11684 | ||
|
|
6589cf9d8e | ||
|
|
552589f098 | ||
|
|
2ec79c03a0 | ||
|
|
21653f0f91 | ||
|
|
b922eaa754 | ||
|
|
d27da93c33 | ||
|
|
2e6fa7f8a3 | ||
|
|
302184938a | ||
|
|
e5355cc45f | ||
|
|
2fe778507a | ||
|
|
4077422ae2 | ||
|
|
a9ac45c5f0 | ||
|
|
7ef99ee6f3 | ||
|
|
e44aada634 | ||
|
|
ac0a864e14 | ||
|
|
3c3331d5ed | ||
|
|
6f21f48937 | ||
|
|
2e227f868b | ||
|
|
7d7513c71c | ||
|
|
d95f1e9913 | ||
|
|
1013b49ab7 | ||
|
|
dc0e15a74e | ||
|
|
d6e5ca722f | ||
|
|
cd3ba00479 | ||
|
|
c549dba725 | ||
|
|
ca1ad99bea | ||
|
|
2c7bd7e591 | ||
|
|
bea45b3c30 | ||
|
|
093c7b4386 | ||
|
|
8332a5aa53 | ||
|
|
eb020656d2 | ||
|
|
31456a6dfe | ||
|
|
03fda0f803 | ||
|
|
c09f03f6e8 | ||
|
|
3bad9ff50a | ||
|
|
0663421d01 | ||
|
|
10175caa38 | ||
|
|
103ad75c50 | ||
|
|
2f5f86d6fc | ||
|
|
89d2c2917f | ||
|
|
05817aa3b9 | ||
|
|
5464ed6661 | ||
|
|
05c84a147d | ||
|
|
9c35403b98 | ||
|
|
b5b075eb04 | ||
|
|
e1261c9620 | ||
|
|
5531bca078 | ||
|
|
28b9dd50ff | ||
|
|
44c2a0a7fc | ||
|
|
d143acfc76 | ||
|
|
b4d59fd52d | ||
|
|
2ee2a1b730 | ||
|
|
86ebc8e46c | ||
|
|
29c5ee6b19 | ||
|
|
323aebf1f5 | ||
|
|
a5dd1ccfaa | ||
|
|
4dd297cef2 | ||
|
|
7f9fea18ae | ||
|
|
adb8b881b4 | ||
|
|
d3d1b69c7a | ||
|
|
922d3716ff | ||
|
|
4158222e86 | ||
|
|
6727c43441 | ||
|
|
55c1ec2925 | ||
|
|
0f1c18e675 | ||
|
|
0de1293ff5 | ||
|
|
89ab390719 | ||
|
|
88ef3ffa50 | ||
|
|
668d5d9d92 | ||
|
|
6c908ac727 | ||
|
|
2c4bd7162f | ||
|
|
cf18e0776d | ||
|
|
8b866beaeb | ||
|
|
ee5fca7a2b | ||
|
|
fc074937f5 | ||
|
|
a17ea13453 | ||
|
|
8bb4ea85b8 | ||
|
|
26e8372c75 | ||
|
|
4704efa32a | ||
|
|
339925e835 | ||
|
|
28360a7f26 | ||
|
|
49bb62d221 | ||
|
|
04f121b88f | ||
|
|
6ce5c8eb78 | ||
|
|
7736cdae1c | ||
|
|
05aa1f1ae4 | ||
|
|
42827cdea2 | ||
|
|
b88dad4034 | ||
|
|
d0645273df | ||
|
|
dea27e105d | ||
|
|
0bfad4392a | ||
|
|
4c1b507c07 | ||
|
|
0289055948 | ||
|
|
83a48dafea | ||
|
|
a7a20a9de4 | ||
|
|
82b67b01ff | ||
|
|
b896203dcf | ||
|
|
795d5807b5 | ||
|
|
7d43f4a1f4 | ||
|
|
c268c5e07c | ||
|
|
517659640e | ||
|
|
a9672e1a21 | ||
|
|
4570f71f21 | ||
|
|
ca2fd867b8 | ||
|
|
624da03bad | ||
|
|
a820f32ec9 | ||
|
|
f5dc6e80c0 | ||
|
|
de70b710af | ||
|
|
bf10a27db8 | ||
|
|
aa2656cb9e | ||
|
|
adf2b2bf03 | ||
|
|
6773326d96 | ||
|
|
1d7efce0dc | ||
|
|
1a1deb5d58 | ||
|
|
bcba806ef0 | ||
|
|
00648c27eb | ||
|
|
4885b39121 | ||
|
|
0f3e70b679 | ||
|
|
372a0f9eea | ||
|
|
2a44bad2e7 | ||
|
|
33e8472473 | ||
|
|
8834a8b216 | ||
|
|
db794c2d32 | ||
|
|
a1d80c29de | ||
|
|
bea796ebc0 | ||
|
|
d4978210f0 | ||
|
|
ef59e952d8 | ||
|
|
54d5ee5562 | ||
|
|
eb75ae05c6 | ||
|
|
b56a1501d2 | ||
|
|
fe9c716fbe | ||
|
|
bc308c21fc | ||
|
|
3e41f36ef5 | ||
|
|
8d2131884c | ||
|
|
289566afe8 | ||
|
|
944adc56ee | ||
|
|
6e81f722ad | ||
|
|
bfa28f4c30 | ||
|
|
3dcd33b08f | ||
|
|
3ac81e9d7c | ||
|
|
1704d21356 | ||
|
|
d690775ca1 | ||
|
|
fb07a28c99 | ||
|
|
1dff9aea62 | ||
|
|
e5d54f5344 | ||
|
|
a558c01589 | ||
|
|
7043983acc | ||
|
|
dd245c63d7 | ||
|
|
84d8760561 | ||
|
|
e1225cec5f | ||
|
|
5db6bb02d6 | ||
|
|
679d3fd5b3 | ||
|
|
c622985fda | ||
|
|
d910786034 | ||
|
|
8dbf572ea5 | ||
|
|
f8af1a7443 | ||
|
|
d95bcab053 | ||
|
|
2b4c10e705 | ||
|
|
ac4c802089 | ||
|
|
c6e4b8f829 | ||
|
|
52d41d19d3 | ||
|
|
0e7df4b193 | ||
|
|
2c9b3b35d1 | ||
|
|
773dcae2f9 | ||
|
|
22e7b674ed | ||
|
|
8c95422204 | ||
|
|
34531a8160 | ||
|
|
5c199483cd | ||
|
|
c52189557e | ||
|
|
bcabe63971 | ||
|
|
193ccaa3b4 | ||
|
|
c6605766e7 | ||
|
|
4fca628d36 | ||
|
|
7f81c62bf0 | ||
|
|
fc7da615fb | ||
|
|
86766f4e20 | ||
|
|
8cb0b7d043 | ||
|
|
b6cdeeef8e | ||
|
|
ac2175fa7d | ||
|
|
5816bab6a2 | ||
|
|
d0d650aa67 | ||
|
|
6d5ec9c998 | ||
|
|
fe08467e45 | ||
|
|
26e03b81fe | ||
|
|
f6582fd229 | ||
|
|
5e87126c38 | ||
|
|
c279692cfe | ||
|
|
7d0dab9d7d | ||
|
|
16555ad12e | ||
|
|
9a95c2ff08 | ||
|
|
1d6347c9b0 | ||
|
|
13075460ea | ||
|
|
4c3dfb92b7 | ||
|
|
fc2efaeb8c | ||
|
|
b15e60243b | ||
|
|
f2de3215c3 | ||
|
|
efbad07643 | ||
|
|
69d61eda01 | ||
|
|
e9255d3714 | ||
|
|
c8647c0396 | ||
|
|
8114627d6a | ||
|
|
5048a30fc7 | ||
|
|
ce6fb83ef4 | ||
|
|
85f8a59d7f | ||
|
|
dd8729063c | ||
|
|
14c766e750 | ||
|
|
3a11a8a01d | ||
|
|
988815579a | ||
|
|
d1fa13ed52 | ||
|
|
08b497faea | ||
|
|
96d332e234 | ||
|
|
440f31a4d3 | ||
|
|
cb91705e03 | ||
|
|
d12e742373 | ||
|
|
7aa7c0f481 | ||
|
|
b3ffa16315 | ||
|
|
09d1f55bce | ||
|
|
21333d0f18 | ||
|
|
edcc859b58 | ||
|
|
2ce17a724a | ||
|
|
0fb07ba328 | ||
|
|
78f0307246 | ||
|
|
c5203ab5ea | ||
|
|
3d1a306518 | ||
|
|
2750d61e92 | ||
|
|
43ac5ad70d | ||
|
|
309e936ad9 | ||
|
|
d00fa906cf | ||
|
|
01839b9c14 | ||
|
|
1f396946e5 | ||
|
|
2f58ef781c | ||
|
|
8b9f70ac08 | ||
|
|
e039d4103f | ||
|
|
0333e81616 | ||
|
|
cf1be4ae2d | ||
|
|
dc34636598 | ||
|
|
c74d881d3d | ||
|
|
ab51f3bec0 | ||
|
|
b4c4fae3f5 | ||
|
|
ce98df59f1 | ||
|
|
f29bdd21a7 | ||
|
|
9d31401d47 | ||
|
|
5c68bbe16f | ||
|
|
07c610e068 | ||
|
|
998f109fc8 | ||
|
|
cfb70bccc1 | ||
|
|
7524f9006e | ||
|
|
bcfe3dacc6 | ||
|
|
6c56151d27 | ||
|
|
98274d2145 | ||
|
|
1d125f55ba | ||
|
|
918470affc | ||
|
|
797f26e45b | ||
|
|
e145c1d31c | ||
|
|
bb5d0c5c4a | ||
|
|
0aea1b608a | ||
|
|
a2aa43a426 | ||
|
|
8d5514603e | ||
|
|
b9a2feff5e | ||
|
|
f13615d97a | ||
|
|
d6227aece6 | ||
|
|
6058389974 | ||
|
|
dccad08508 | ||
|
|
112158b056 | ||
|
|
5be300736e | ||
|
|
1b98a64e90 | ||
|
|
4849eb4c54 | ||
|
|
5e8ee7e006 | ||
|
|
e27d34d118 | ||
|
|
a0bf8d5740 | ||
|
|
17a5ba08e4 | ||
|
|
d0877153bf | ||
|
|
55011e5308 | ||
|
|
2697b3c7da | ||
|
|
b9c17dd1f0 | ||
|
|
aea17bfae2 | ||
|
|
0a5755145c | ||
|
|
249e19769b | ||
|
|
f64808c385 | ||
|
|
2e69b54a4f | ||
|
|
cd5d1ceadf | ||
|
|
c55f6b40ec | ||
|
|
bf4ec9be22 | ||
|
|
ed0626afeb | ||
|
|
b5305d5a8c | ||
|
|
17efa04ea6 | ||
|
|
b3f5c8f6b2 | ||
|
|
4507083148 | ||
|
|
f4c0018d7a | ||
|
|
c9e1d6cca8 | ||
|
|
242dc34115 | ||
|
|
41c785d77a | ||
|
|
a745528b40 | ||
|
|
7465be91d0 | ||
|
|
d79c5baa1b | ||
|
|
46af0d38fa | ||
|
|
6323e09a0a | ||
|
|
d94bdb75c8 | ||
|
|
cc3b4dff20 | ||
|
|
78ac382fdf | ||
|
|
860072aaaf | ||
|
|
2694a409c6 | ||
|
|
206b1f4631 | ||
|
|
ef9891e8ff | ||
|
|
bd69890ccd | ||
|
|
fc280c7cd6 | ||
|
|
435ac2a650 | ||
|
|
fb0ad2f9ea | ||
|
|
7385449f33 | ||
|
|
1f4b814d0b | ||
|
|
a93d9c4310 | ||
|
|
1813d11b9d | ||
|
|
09ac68c196 | ||
|
|
c2fd905e32 | ||
|
|
cc84f28d1b | ||
|
|
2bd498524d | ||
|
|
4b934945f3 | ||
|
|
b666ab0673 | ||
|
|
6a4c30fe5d | ||
|
|
4002aef594 | ||
|
|
ba28d36e67 | ||
|
|
4444db2990 | ||
|
|
d0026793bf | ||
|
|
93b5cd5ddd | ||
|
|
12c2d7e4cf | ||
|
|
4eb5acd9e2 | ||
|
|
0f56d838ec | ||
|
|
12a948dacb | ||
|
|
137953605e | ||
|
|
629fad6f5f | ||
|
|
2911c84a69 | ||
|
|
89a1420609 | ||
|
|
e584557afe | ||
|
|
2ed5413e24 | ||
|
|
91532b2d5c | ||
|
|
732cac1c0e | ||
|
|
5b04860b8b | ||
|
|
5a674f419d | ||
|
|
d30246dc05 | ||
|
|
af5a913019 | ||
|
|
604e69c7ae | ||
|
|
d765c9de37 | ||
|
|
98b6e2bcce | ||
|
|
d80be7961c | ||
|
|
2aec3a9789 | ||
|
|
ccd7dbe0e4 | ||
|
|
92fcfd7a6f | ||
|
|
1b2b054139 | ||
|
|
853d1eac96 | ||
|
|
2ef29d6839 | ||
|
|
6865806b55 | ||
|
|
9546e65617 | ||
|
|
5852993243 | ||
|
|
90a03a77ad | ||
|
|
4bfbe7ebeb | ||
|
|
f3c1686a40 | ||
|
|
33a587d97e | ||
|
|
a444ec5111 | ||
|
|
5ead1bf5c8 | ||
|
|
3a267586f4 | ||
|
|
52271e0366 | ||
|
|
e4739da961 | ||
|
|
80b344e9fc | ||
|
|
036cb26fa3 | ||
|
|
33d6dcec5b | ||
|
|
791505ab6e | ||
|
|
88e1719d8e | ||
|
|
0885555521 | ||
|
|
36c1750b8f | ||
|
|
11b1c9f19f | ||
|
|
b5dd87b184 | ||
|
|
b54c7bedfd | ||
|
|
783404d8d4 | ||
|
|
3bad9f013e | ||
|
|
4495516497 | ||
|
|
3f849848a4 | ||
|
|
7203680146 | ||
|
|
d793ed3b9b | ||
|
|
c6de17d1e5 | ||
|
|
5b084f4b95 | ||
|
|
4e3b2b9f6b | ||
|
|
856ccb5bb7 | ||
|
|
dbf5659cd9 | ||
|
|
fee0c6afe9 | ||
|
|
fb7d3a159a | ||
|
|
dc6b4e65e0 | ||
|
|
17d4ef4053 | ||
|
|
03be8f88fe | ||
|
|
effcb90fdf | ||
|
|
1bd06bee82 | ||
|
|
92cdc1e33f | ||
|
|
377628216e | ||
|
|
f7627929d5 | ||
|
|
b49cf5e7a2 | ||
|
|
231a6cecb4 | ||
|
|
32f02325f9 | ||
|
|
f418782c2c | ||
|
|
e52730d1fb | ||
|
|
d2679e58a6 | ||
|
|
e3817cd549 | ||
|
|
4ace50a7d7 | ||
|
|
fa092c47c8 | ||
|
|
704d90f693 | ||
|
|
93dc36e091 | ||
|
|
4cc36b2284 | ||
|
|
aaa8d2795e | ||
|
|
229137cad1 | ||
|
|
d73262a0e5 | ||
|
|
cd22a8d851 | ||
|
|
7f7df4ae19 | ||
|
|
3c5723e49d | ||
|
|
960fb14bf5 | ||
|
|
21e17f649a | ||
|
|
9782c40d21 | ||
|
|
04beab4f7d | ||
|
|
194a85387b | ||
|
|
a99219ad1f | ||
|
|
691410ba18 | ||
|
|
714a43b18c | ||
|
|
9f457d3331 | ||
|
|
60a456f757 | ||
|
|
2b355d6b34 | ||
|
|
48be546357 | ||
|
|
0f74a57267 | ||
|
|
272270c7f4 | ||
|
|
e2f014f457 | ||
|
|
1fba33628d | ||
|
|
7d604a23f0 | ||
|
|
6929a786aa | ||
|
|
f61b80ca6a | ||
|
|
1aba9f031d | ||
|
|
2ffcc52f03 | ||
|
|
72518a0d6e | ||
|
|
1242575f0f | ||
|
|
6ccc2ef804 | ||
|
|
b3dc6f2b5b | ||
|
|
2427917db8 | ||
|
|
5ab40f016d | ||
|
|
435115d4fb | ||
|
|
24d823dae6 | ||
|
|
2c60a48d02 | ||
|
|
4b0be4a477 | ||
|
|
505dd10d3c | ||
|
|
ddd2b69f27 | ||
|
|
66b57f89df | ||
|
|
69bd5a29d9 | ||
|
|
f7479781c1 | ||
|
|
9bfeb4e5af | ||
|
|
d3e5ade2da | ||
|
|
3833dd86aa | ||
|
|
25f33c8444 | ||
|
|
dae247809b | ||
|
|
81df8bdabd | ||
|
|
03e4eb4de8 | ||
|
|
290bd400c3 | ||
|
|
5d0785e47a | ||
|
|
16434568b0 | ||
|
|
c0e7d54e37 | ||
|
|
ccb53d9e7d | ||
|
|
03bd61c799 | ||
|
|
6ffd29507c | ||
|
|
a880709776 | ||
|
|
874d1641df | ||
|
|
d8d964d09e | ||
|
|
46b3cb580d | ||
|
|
02f5e19420 | ||
|
|
a945bf07af | ||
|
|
7e5b0a9eb6 | ||
|
|
a56795c149 | ||
|
|
baeb41f5f4 | ||
|
|
d0348e0c60 | ||
|
|
ef0a7ecbcd | ||
|
|
778c65d27c | ||
|
|
f3face9f0c | ||
|
|
2edd5230c9 | ||
|
|
1551bb702d | ||
|
|
d656e9f28e | ||
|
|
597764e35b | ||
|
|
f9f77876a1 | ||
|
|
67d8c7999a | ||
|
|
a7fdb67e8f | ||
|
|
4d1ff2418e | ||
|
|
71f5fdef42 | ||
|
|
abc36471d9 | ||
|
|
4b7288e8f4 | ||
|
|
108ed3c81a | ||
|
|
f98e5717e5 | ||
|
|
3685bc4828 | ||
|
|
72c580236e | ||
|
|
01d2a87f5d | ||
|
|
51061fb61e | ||
|
|
49e8e28cf6 | ||
|
|
d2c632fb18 | ||
|
|
3562be2188 | ||
|
|
2e18a9be63 | ||
|
|
3347762df3 | ||
|
|
f1bc73c3ed | ||
|
|
a96fa7d221 | ||
|
|
cc3e01a9f5 | ||
|
|
6caa389c48 | ||
|
|
e86446b785 | ||
|
|
a00448c35a | ||
|
|
82b9152b8d | ||
|
|
d778fbb24f | ||
|
|
3803a0021d | ||
|
|
3c96819a2c | ||
|
|
846a2fb933 | ||
|
|
7e9eb55350 | ||
|
|
46b7bd6ec3 | ||
|
|
8bbe3d2fe0 | ||
|
|
25f59761b3 | ||
|
|
4b038f13de | ||
|
|
da3d84d1dc | ||
|
|
5ef976aed5 | ||
|
|
cf0b831971 | ||
|
|
d861daeb4e | ||
|
|
0a2eb3fdc8 | ||
|
|
6fb248c9e0 | ||
|
|
40946a79d3 | ||
|
|
124ca0fed2 | ||
|
|
5d153ca1b3 | ||
|
|
a5b0ea97d6 | ||
|
|
0a49f2e068 | ||
|
|
57594ad7b6 | ||
|
|
75bb43698c | ||
|
|
f70014b8ea | ||
|
|
5fd410db4b | ||
|
|
385d044f7d | ||
|
|
d0f1c552e2 | ||
|
|
a54bb7205d | ||
|
|
7466474365 | ||
|
|
c4bc9ce59a | ||
|
|
e25c9f7951 | ||
|
|
94cbed5096 | ||
|
|
f83ace2034 | ||
|
|
84ed9de21a | ||
|
|
4e764017c1 | ||
|
|
67f5e17588 | ||
|
|
d13ac66cc4 | ||
|
|
9f0b7261e5 | ||
|
|
ad4a869b94 | ||
|
|
79d3b41e13 | ||
|
|
839befe68a | ||
|
|
097e8c8eb4 | ||
|
|
90c7d3aaff | ||
|
|
2621e3fa19 | ||
|
|
36d7f50d77 | ||
|
|
ffbb582dde | ||
|
|
3bfc07eb92 | ||
|
|
d91caf1cd3 | ||
|
|
2a2be49d2c | ||
|
|
d47a04456b | ||
|
|
b9499d07c2 | ||
|
|
5d20e690fd | ||
|
|
1c23bfdc7f | ||
|
|
d5531a239b | ||
|
|
9e37d70163 | ||
|
|
99ff7efe80 | ||
|
|
43a08bef14 | ||
|
|
92beddd72f | ||
|
|
f5b783d107 | ||
|
|
a5a870c683 | ||
|
|
19ab25c861 | ||
|
|
38fcfcdfac | ||
|
|
957f4d8096 | ||
|
|
5d4438e652 | ||
|
|
4649c14856 | ||
|
|
fcf6434b4b | ||
|
|
464950d51e | ||
|
|
0b5d60db5e | ||
|
|
b6542b977e | ||
|
|
2bf1184c39 | ||
|
|
103b5a760f | ||
|
|
2b1c389955 | ||
|
|
8d9fa63460 | ||
|
|
52b87b751f | ||
|
|
e934f8aac6 | ||
|
|
311d16df7d | ||
|
|
ea78d69c6d | ||
|
|
402597c2cb | ||
|
|
68678e21df | ||
|
|
19a9b52c06 | ||
|
|
db38ee6630 | ||
|
|
1d8538bca4 | ||
|
|
b73dcfb8cf | ||
|
|
bcd4b76d31 | ||
|
|
7c57ee0c17 | ||
|
|
d9157a71bb | ||
|
|
eae9798276 | ||
|
|
e131d3e32a | ||
|
|
36043a92e7 | ||
|
|
9b48657444 | ||
|
|
52970c2459 | ||
|
|
8336796c1a | ||
|
|
16a63c6554 | ||
|
|
c637441f52 | ||
|
|
5b221ecd4f | ||
|
|
7af77aefa7 | ||
|
|
7c2054091d | ||
|
|
e04e0cb98e | ||
|
|
e983865ad5 | ||
|
|
4c9fe60693 | ||
|
|
c5dfa4099d | ||
|
|
524b7bc36b | ||
|
|
8908055b63 | ||
|
|
10abe8aba2 | ||
|
|
7b24a6360c | ||
|
|
96e9689dbd | ||
|
|
7e0204bb7b | ||
|
|
dfb80c6b86 | ||
|
|
838c35acf6 | ||
|
|
c858aa5686 | ||
|
|
b1736b13d7 | ||
|
|
cbb86fe590 | ||
|
|
a61f5f3a78 | ||
|
|
0792effc2a | ||
|
|
49c6ce3703 | ||
|
|
0f05a6aede | ||
|
|
7ccf8cb731 | ||
|
|
7c28c66d32 | ||
|
|
c49a3ab2d6 | ||
|
|
8fbaf0304a | ||
|
|
f89c08872c | ||
|
|
29eb56a2f6 | ||
|
|
2e795d2706 | ||
|
|
8768e4488a | ||
|
|
bd3f86e505 | ||
|
|
82e4716784 | ||
|
|
d3635c5eef | ||
|
|
c73b1b4fa2 | ||
|
|
39df7d048c | ||
|
|
3529349470 | ||
|
|
0030d1ade5 | ||
|
|
2d1631792a | ||
|
|
b6babe38ea | ||
|
|
df7e435b86 | ||
|
|
9e59f661bc | ||
|
|
87c2d911c9 | ||
|
|
44af02aa1e | ||
|
|
45e51d1929 | ||
|
|
59a623e648 | ||
|
|
306f2203fa | ||
|
|
7699b59e27 | ||
|
|
1550c4215d | ||
|
|
a84590273d | ||
|
|
6fe258c6bc | ||
|
|
27c1c7a0f7 | ||
|
|
a668a4e4d1 | ||
|
|
b1250f003d | ||
|
|
3f8e6c423c | ||
|
|
077caac76d | ||
|
|
91d70a3364 | ||
|
|
6af20340dd | ||
|
|
68ab197b68 | ||
|
|
79e6b4c819 | ||
|
|
ececc3ca45 | ||
|
|
630f575017 | ||
|
|
49274c1507 | ||
|
|
27d4cf6aef | ||
|
|
b156d392cb | ||
|
|
b0b4290c29 | ||
|
|
90bed0c5ac | ||
|
|
b3caddab30 | ||
|
|
1017279e68 | ||
|
|
f74cebcbcd | ||
|
|
848debf999 | ||
|
|
47756a8faa | ||
|
|
6c027c153b | ||
|
|
28dc5f491c | ||
|
|
758e99f362 | ||
|
|
010dc6258e | ||
|
|
a5c5949009 | ||
|
|
d51d6b2d89 | ||
|
|
c58dfa2156 | ||
|
|
5d360a6711 | ||
|
|
24fcf9a86e | ||
|
|
a23b446ddf | ||
|
|
258fe88232 | ||
|
|
9ff7d29696 | ||
|
|
30d69d68e3 | ||
|
|
4495d9ef5b | ||
|
|
a66039b86c | ||
|
|
81c5d9e983 | ||
|
|
c6f7a34abe | ||
|
|
72d6f1a418 | ||
|
|
8cb3ea1398 | ||
|
|
8712deb0b2 | ||
|
|
2e8de4f601 | ||
|
|
c22d3b4c98 | ||
|
|
f8c22c7428 | ||
|
|
855dcadf7a | ||
|
|
9c41e1eea3 | ||
|
|
c70c3eac62 | ||
|
|
e58f4c567a | ||
|
|
1dd8300e69 | ||
|
|
5fa0347e64 | ||
|
|
32c16fad42 | ||
|
|
ea3b8f52d5 | ||
|
|
95445fc328 | ||
|
|
21e3cde95c | ||
|
|
d587940801 | ||
|
|
f5d4f3d83c | ||
|
|
04a8b0f85d | ||
|
|
853e33bdb4 | ||
|
|
0644c4c08e | ||
|
|
606fed8896 | ||
|
|
a5463f0400 | ||
|
|
96338314f1 | ||
|
|
5d3cab0042 | ||
|
|
e678e1bdcb | ||
|
|
1811a344f5 | ||
|
|
ef31aa12e2 | ||
|
|
9dd6c28523 | ||
|
|
61e5301d49 | ||
|
|
408829bcf6 | ||
|
|
dbcce3a4be | ||
|
|
426dd9bfad | ||
|
|
82318c0ef1 | ||
|
|
60696a8485 | ||
|
|
7044385d8e | ||
|
|
a7139ef06b | ||
|
|
52389fc09a | ||
|
|
972ca9e6c1 | ||
|
|
e0c5c4de88 | ||
|
|
149f21dc58 | ||
|
|
9632632b85 | ||
|
|
cc1e9370af | ||
|
|
34139606ca | ||
|
|
3e1cca65bd | ||
|
|
0e3c256667 | ||
|
|
618355f5a7 | ||
|
|
ca11baf1cd | ||
|
|
7ede02ca8b | ||
|
|
2d4e76874c | ||
|
|
b463562241 | ||
|
|
04f17c4709 | ||
|
|
97f797e859 | ||
|
|
9521f54741 | ||
|
|
0e38a52edc | ||
|
|
6df94d3c76 | ||
|
|
59005b544f | ||
|
|
8596bb7e7e | ||
|
|
1c47d15577 | ||
|
|
67c587f9f7 | ||
|
|
052be0a328 | ||
|
|
68ddcc6ca7 | ||
|
|
9a2d97ec41 | ||
|
|
47e770dffa | ||
|
|
8292cebfd9 | ||
|
|
c483c13dc4 | ||
|
|
86f2942c2a | ||
|
|
e4d19dc4a0 | ||
|
|
763760503b | ||
|
|
13c9d2dade | ||
|
|
97f6c5b239 | ||
|
|
f4a9d173a1 | ||
|
|
d85a18752f | ||
|
|
aa7dd9a8ad | ||
|
|
27c9670718 | ||
|
|
32833bcd56 | ||
|
|
53bc34f3e2 | ||
|
|
81640c4a83 | ||
|
|
533e6b5f8d | ||
|
|
28f0bbd575 | ||
|
|
39717b2d9d | ||
|
|
79c88cdcfd | ||
|
|
8a34c16aee | ||
|
|
cce192a671 | ||
|
|
cfe4a12a46 | ||
|
|
3269da52cc | ||
|
|
9c51487d1a | ||
|
|
3ba5ddefa7 | ||
|
|
1d45aa8d45 | ||
|
|
a7bcdf4398 | ||
|
|
9a0dac5885 | ||
|
|
c9e0652526 | ||
|
|
a566f9d8f2 | ||
|
|
1740b0d421 | ||
|
|
802a048302 | ||
|
|
fa08823ac4 | ||
|
|
a1c0b9da7b | ||
|
|
bf261f4f13 | ||
|
|
dc8639bc06 | ||
|
|
125efd8396 | ||
|
|
4a9557fa84 | ||
|
|
6b6c82aaee | ||
|
|
feca27d69e | ||
|
|
fbef894674 | ||
|
|
2e4bb8b3b7 | ||
|
|
e9940acb54 | ||
|
|
957331fff4 | ||
|
|
faba395cae | ||
|
|
1d5809679b | ||
|
|
b5014b0966 | ||
|
|
b1ad36eece | ||
|
|
1b42286bda | ||
|
|
5da0620c6b | ||
|
|
96e8f16c05 | ||
|
|
b5d28e0a9c | ||
|
|
73251bc9c1 | ||
|
|
3fa63b35b3 | ||
|
|
65f006e52e | ||
|
|
7d46e06824 | ||
|
|
32acb82bc0 | ||
|
|
3a1c5944e7 | ||
|
|
04c5246e02 | ||
|
|
46dc1c9bfb | ||
|
|
b900253c09 | ||
|
|
59a5fb7ce8 | ||
|
|
c5d2e536cf | ||
|
|
aeef0a21f3 | ||
|
|
902ad897b8 | ||
|
|
478ccee5fc | ||
|
|
43fd5c28b5 | ||
|
|
4391463832 | ||
|
|
c8d2d2a8ea | ||
|
|
6795aabecf | ||
|
|
a939c09a6b | ||
|
|
35d1c33e0a | ||
|
|
10edf64873 | ||
|
|
6d22c38e59 | ||
|
|
8a81cb4ba0 | ||
|
|
6bb7f987f2 | ||
|
|
1c018caefe | ||
|
|
0c136893e5 | ||
|
|
f4c0655190 | ||
|
|
49bdf32765 | ||
|
|
88d02a624a | ||
|
|
98a58f9e67 | ||
|
|
b30c6f8b9e | ||
|
|
94a7fee8c8 | ||
|
|
6f38b63a47 | ||
|
|
70b05c8096 | ||
|
|
28ac4347d4 | ||
|
|
dee1d82c11 | ||
|
|
6d73f82f41 | ||
|
|
71ddff4964 | ||
|
|
af3b796488 | ||
|
|
e2de2c6bb2 | ||
|
|
2ff0ce2d0a | ||
|
|
b09b8a0ccc | ||
|
|
db259c3557 | ||
|
|
bcc87ef851 | ||
|
|
650f16b6d9 | ||
|
|
ae2dd38e0d | ||
|
|
0319c1cf90 | ||
|
|
81f9442ac7 | ||
|
|
18ab274af3 | ||
|
|
65d1f52423 | ||
|
|
f1b82e0e9a | ||
|
|
67411c4299 | ||
|
|
a37e62b7d0 | ||
|
|
e2c1f7d5b5 | ||
|
|
ec97fbd101 | ||
|
|
b7d7559cc9 | ||
|
|
b4404ff0c8 | ||
|
|
502e89f4ad | ||
|
|
d8d8cf7e98 | ||
|
|
d9a5094068 | ||
|
|
a9740c6a44 | ||
|
|
2dc027401f | ||
|
|
ac638c85f3 | ||
|
|
c0e22c255c | ||
|
|
1309e5e86f | ||
|
|
c34c024dd5 | ||
|
|
6ef69ece95 | ||
|
|
a9cbdc141b | ||
|
|
9e5cf1cf8e | ||
|
|
ddc783bee7 | ||
|
|
68f92050e9 | ||
|
|
bd2bc2ee14 | ||
|
|
8a7c1c4926 | ||
|
|
cff3e0cad2 | ||
|
|
b55cfbc342 | ||
|
|
3115087d26 | ||
|
|
057c51ff6d | ||
|
|
f2f20cf133 | ||
|
|
2c618705f0 | ||
|
|
bc10ad2356 | ||
|
|
857fac1168 | ||
|
|
103ba9b0fc | ||
|
|
d83fc6893b | ||
|
|
e7d5739ce7 | ||
|
|
8bf57c0e14 | ||
|
|
dea7255fc5 | ||
|
|
ce3082976c | ||
|
|
362a295e06 | ||
|
|
0e91e2adf2 | ||
|
|
4d49d952b6 | ||
|
|
ae7a7a0abf | ||
|
|
4f265dce60 | ||
|
|
bd96db8c9f | ||
|
|
0b75917121 | ||
|
|
fa2dbc2b1b | ||
|
|
3a6165f0b9 | ||
|
|
62c27f7fce | ||
|
|
7feb116bae | ||
|
|
730a938912 | ||
|
|
3553efb75e | ||
|
|
6111230fe9 | ||
|
|
6bce84e070 | ||
|
|
a6e316a92d | ||
|
|
22bcf5892a | ||
|
|
cc4b968f0a | ||
|
|
893ddcf61b | ||
|
|
cf81dbcca4 | ||
|
|
5830200029 | ||
|
|
972592cf8a | ||
|
|
ce5699f41b | ||
|
|
1eee0be951 | ||
|
|
fb555a0633 | ||
|
|
2b87f899ab | ||
|
|
776e1926a3 | ||
|
|
c232edb83a | ||
|
|
aefeba3fc0 | ||
|
|
6d4064799a | ||
|
|
e0e9a63e63 | ||
|
|
bcdffce6ce | ||
|
|
ff97c8a1a9 | ||
|
|
30acd1662d | ||
|
|
078e2f60d6 | ||
|
|
c7ce2792c8 | ||
|
|
22d6558f13 | ||
|
|
105d72a3c2 | ||
|
|
c752e5731c | ||
|
|
72e9cf53e7 | ||
|
|
e66d315c9b | ||
|
|
95f6dc7011 | ||
|
|
ac9e758f41 | ||
|
|
518b18dad0 | ||
|
|
bdd5586cee | ||
|
|
14dba4ae2b | ||
|
|
99d82c4980 | ||
|
|
fb46e0f192 | ||
|
|
b055128ec4 | ||
|
|
e29f94a980 | ||
|
|
24342eaab0 | ||
|
|
152d22d4f9 | ||
|
|
41eecb67d2 | ||
|
|
9317105c37 | ||
|
|
23d7f5425d | ||
|
|
217f279fdb | ||
|
|
d4ecb23015 | ||
|
|
42c59e1079 | ||
|
|
3b5993e6ee | ||
|
|
4b8255a588 | ||
|
|
36e3d24331 | ||
|
|
261dfdf010 | ||
|
|
cbfacb7ed9 | ||
|
|
4eda29abf9 | ||
|
|
c3f7107e2b | ||
|
|
67a3976a24 | ||
|
|
4fba1b1430 | ||
|
|
029b786c77 | ||
|
|
51e02176da | ||
|
|
ed65d9cd75 | ||
|
|
542b8fffb1 | ||
|
|
12c50ed87b | ||
|
|
9d5bf57dd9 | ||
|
|
04df18bcd3 | ||
|
|
422ba5bcec | ||
|
|
27b28a9306 | ||
|
|
620b3e6739 | ||
|
|
580f334274 | ||
|
|
4f52183a39 | ||
|
|
a55f354687 | ||
|
|
9630c9136b | ||
|
|
dbd6d7ec8d | ||
|
|
b3a5d6df0d | ||
|
|
3587e8481d | ||
|
|
4f8f6ba205 | ||
|
|
f81ed216db | ||
|
|
f1a9f7ea70 | ||
|
|
4b14621208 | ||
|
|
baf30f5eae | ||
|
|
e7a2ab5de9 | ||
|
|
fd41c5de2d | ||
|
|
efac109803 | ||
|
|
84505d4cc8 | ||
|
|
7d8fac62ec | ||
|
|
b6e18d9c6f | ||
|
|
c05e968278 | ||
|
|
103fe60362 | ||
|
|
638020ec70 | ||
|
|
56038b6457 | ||
|
|
96b788bea7 | ||
|
|
7b6c11360f | ||
|
|
ea1eb45e3f | ||
|
|
e001a021d5 | ||
|
|
14cb4eb735 | ||
|
|
d976c8e85f | ||
|
|
d2d33cb984 | ||
|
|
eeae59ecb7 | ||
|
|
466f4e699c | ||
|
|
bf09400c4c | ||
|
|
c4509e700d | ||
|
|
6472e3b35e | ||
|
|
f9bd5019fa | ||
|
|
8c7705bbf9 | ||
|
|
243e3d0d91 | ||
|
|
19884f40b8 | ||
|
|
10c3ce08af | ||
|
|
878532a30d | ||
|
|
102d69004a | ||
|
|
b0c61ffbb1 | ||
|
|
9f96f09652 | ||
|
|
aa568727d1 | ||
|
|
411cb39eb4 | ||
|
|
6777ae2ada | ||
|
|
2cf3e6e9bc | ||
|
|
491b73d5f2 | ||
|
|
1f3fe200df | ||
|
|
299e2a7077 | ||
|
|
13a139eef7 | ||
|
|
06df1647e1 | ||
|
|
1d2804d6a2 | ||
|
|
ba98bcdc60 | ||
|
|
0d7dccba5e | ||
|
|
c231e95cce | ||
|
|
6f6c8c5434 | ||
|
|
edf72a7dfb | ||
|
|
ba636e19a2 | ||
|
|
6374766a55 | ||
|
|
c779a9f29f | ||
|
|
ae2d5266c5 | ||
|
|
99db244124 | ||
|
|
8367feafa9 | ||
|
|
f6a28808e3 | ||
|
|
13d8ae859d | ||
|
|
95b15fe463 | ||
|
|
779938ea68 | ||
|
|
139944a3ac | ||
|
|
b94e3a874d | ||
|
|
3d8e5003af | ||
|
|
94cd14e90d | ||
|
|
c93cfb2c74 | ||
|
|
521d439a07 | ||
|
|
460b18e82c | ||
|
|
ef3666ef26 | ||
|
|
e0aaa87b1f | ||
|
|
eba83ab740 | ||
|
|
a8141c53a8 | ||
|
|
88ff8c83e9 | ||
|
|
24e0b25215 | ||
|
|
7bc881ccc5 | ||
|
|
a3bc6cd666 | ||
|
|
40d4a8fc2f | ||
|
|
7be9e292dc | ||
|
|
9ccd31a12e | ||
|
|
a9aefcc1be | ||
|
|
05587f5da9 | ||
|
|
43dd38df0a | ||
|
|
64fa8d78bc | ||
|
|
aee9c4c8e6 | ||
|
|
bcb4afa886 | ||
|
|
7774b57a1c | ||
|
|
90cb4b4e09 | ||
|
|
7fa9010f35 | ||
|
|
03f888c968 | ||
|
|
e07d71f5e8 | ||
|
|
afcf49ad3a | ||
|
|
a2f1892480 | ||
|
|
552106e2a4 | ||
|
|
a48370d5e9 | ||
|
|
f067d34392 | ||
|
|
30d4c243ec | ||
|
|
2637f2ad3a | ||
|
|
9b92f4c882 | ||
|
|
42344ce20c | ||
|
|
faca23b984 | ||
|
|
0cbd7501a1 | ||
|
|
03aa8270a6 | ||
|
|
ef70313e05 | ||
|
|
2ec3c46838 | ||
|
|
52e8ec03ba | ||
|
|
c54777d957 | ||
|
|
7b11fb75d9 | ||
|
|
e937521014 | ||
|
|
f02c39a402 | ||
|
|
c580c2be75 | ||
|
|
8d0f689cb5 | ||
|
|
bfd96264e6 | ||
|
|
baab7c04f7 | ||
|
|
3572ec784d | ||
|
|
6f00cbb6ef | ||
|
|
7518acf634 | ||
|
|
52e6922e5a | ||
|
|
82ddf82866 | ||
|
|
55bcc20deb | ||
|
|
9906e4c984 | ||
|
|
886cfbe12d | ||
|
|
4e40fee1db | ||
|
|
bceff76c70 | ||
|
|
b333b7c9dc | ||
|
|
de4b9b584c | ||
|
|
bf924118de | ||
|
|
a41f113b8f | ||
|
|
5941b3c86e | ||
|
|
caa1834b70 | ||
|
|
c61262d839 | ||
|
|
9c4daa312d | ||
|
|
32005617b9 | ||
|
|
1dfa246674 | ||
|
|
7e616ea71c | ||
|
|
99afb15ced | ||
|
|
22e2540eb6 | ||
|
|
8d39b1a152 | ||
|
|
1ed34774c8 | ||
|
|
48e62e25e9 | ||
|
|
1cb470662b | ||
|
|
add4874801 | ||
|
|
8cc32bc29c | ||
|
|
fc2d28b851 | ||
|
|
a3ef33c56f | ||
|
|
ba4254231e | ||
|
|
2b54da5070 | ||
|
|
d2fa5c91a7 | ||
|
|
4653974c2a | ||
|
|
de11e7fd75 | ||
|
|
1dd1064051 | ||
|
|
ac10470643 | ||
|
|
8d3d08e814 | ||
|
|
3f96a15fd4 | ||
|
|
22f51f953b | ||
|
|
f3f7bd00e5 | ||
|
|
a72e4a406f | ||
|
|
2fe1254aae | ||
|
|
e4590b26fb | ||
|
|
325d6e7cfa | ||
|
|
6bce832060 | ||
|
|
37a5a87057 | ||
|
|
865f6eb5f3 | ||
|
|
dde6ddfd7c | ||
|
|
47d8d69b81 | ||
|
|
df1b7ea311 | ||
|
|
113a52d091 | ||
|
|
081e4d9209 | ||
|
|
e5a98b6a5b | ||
|
|
2942157ca4 | ||
|
|
6746beb2a3 | ||
|
|
92aa004b75 | ||
|
|
b28cd02149 | ||
|
|
cedd5c21e6 | ||
|
|
b714cd5f4b | ||
|
|
788892ee7c | ||
|
|
1b07fecbf3 | ||
|
|
4a01122fb7 | ||
|
|
4211da00b3 | ||
|
|
7f3cff6e09 | ||
|
|
6e612e5def | ||
|
|
a7718b2ef3 | ||
|
|
472407c719 | ||
|
|
f07c1cf874 | ||
|
|
ce7c311ecf | ||
|
|
499c63bd0a | ||
|
|
03777ccb21 | ||
|
|
b4741332e0 | ||
|
|
3874f5f5e8 | ||
|
|
8c19f9a57e | ||
|
|
f57c2424cf | ||
|
|
181b68b01e | ||
|
|
93d5bed697 | ||
|
|
5743b11925 | ||
|
|
4dcfdac2f5 | ||
|
|
9343b767f8 | ||
|
|
77d9233427 | ||
|
|
f63a3c1f62 | ||
|
|
511cdfb951 | ||
|
|
e1f49d990b | ||
|
|
1b208d0954 | ||
|
|
4ac4d56b53 | ||
|
|
c7a0f32c98 | ||
|
|
be0686d905 | ||
|
|
9940b049ec | ||
|
|
3d9c76a69c | ||
|
|
7797ada1f5 | ||
|
|
3fc883e8fb | ||
|
|
8cfd923aaa | ||
|
|
c86b446d4c | ||
|
|
32a09370e1 | ||
|
|
32c6893081 | ||
|
|
95425552d7 | ||
|
|
dc515c7ad3 | ||
|
|
c31b405af6 | ||
|
|
e7483f2e64 | ||
|
|
9a524dac86 | ||
|
|
b3c52f574a | ||
|
|
f58515b51e | ||
|
|
da4df1f414 | ||
|
|
7f0640b040 | ||
|
|
8c0e6de00a | ||
|
|
8ff1194b6a | ||
|
|
9dd4ad6c1f | ||
|
|
4e398e2aa9 | ||
|
|
92401faa72 | ||
|
|
edc46281a4 | ||
|
|
6c8b3e3b58 | ||
|
|
1faf0ab9e3 | ||
|
|
e6ec23d7cc | ||
|
|
e2cd201f6a | ||
|
|
7e447c83fb | ||
|
|
0c0e8d6544 | ||
|
|
a4d14722f3 | ||
|
|
8020ba2dc5 | ||
|
|
21bbc9a81a | ||
|
|
bfe24b138d | ||
|
|
f3dbd986cb | ||
|
|
ecce865cfe | ||
|
|
4a6f60937a | ||
|
|
cbe9b050a9 | ||
|
|
2d5908f4de | ||
|
|
298d5cf042 | ||
|
|
853765954e | ||
|
|
7a99d73092 | ||
|
|
7077d51f1f | ||
|
|
270c7c1110 | ||
|
|
2d3a64815f | ||
|
|
86dc53b72a | ||
|
|
649d43c2c5 | ||
|
|
6d9b44e243 | ||
|
|
e864ba6c91 | ||
|
|
0686380302 | ||
|
|
282e9ead17 | ||
|
|
1f08a5e5f5 | ||
|
|
c41508cc8d | ||
|
|
187df4464e | ||
|
|
c94d640e2a | ||
|
|
cf998b7bf8 | ||
|
|
1a104762ea | ||
|
|
2a5209bb5a | ||
|
|
ebd167b658 | ||
|
|
c13ec867c9 | ||
|
|
199648ff37 | ||
|
|
536f2e42d2 | ||
|
|
0f9799d7e0 | ||
|
|
b1a4b00091 | ||
|
|
c1c9983cfe | ||
|
|
8980c3f74e | ||
|
|
418f57dd6a | ||
|
|
9ef005454a | ||
|
|
afad35a8ac | ||
|
|
2ca113a218 | ||
|
|
911ee4d9f9 | ||
|
|
9804b25acc | ||
|
|
87e16a8f47 | ||
|
|
e561c7b49d | ||
|
|
8cd65eaf46 | ||
|
|
b7ef6077df | ||
|
|
cb71d6d393 | ||
|
|
79c00be516 | ||
|
|
949efb414a | ||
|
|
5dd1256769 | ||
|
|
c52ef1d2b3 | ||
|
|
c7e91d3302 | ||
|
|
c1bbcf5ed3 | ||
|
|
87c5a61d07 | ||
|
|
0aa3d89134 | ||
|
|
4ffafdeb86 | ||
|
|
0bf32b957d | ||
|
|
2bdd916b06 | ||
|
|
93c487fb75 | ||
|
|
036d1aae50 | ||
|
|
f600a868f5 | ||
|
|
95f2d48ac4 | ||
|
|
8c847a85ae | ||
|
|
c265fa3331 | ||
|
|
6aab0332e8 | ||
|
|
47ae677652 | ||
|
|
54e1b79ac5 | ||
|
|
6afe1f3597 | ||
|
|
4466260480 | ||
|
|
1c6e31173d | ||
|
|
939180c1a1 | ||
|
|
8205737fdb | ||
|
|
75d4c9b446 | ||
|
|
e3cd1ac1f9 | ||
|
|
027a70a415 | ||
|
|
3dfe5f993b | ||
|
|
f5991dd9b6 | ||
|
|
58c5d008d5 | ||
|
|
ac1149156d | ||
|
|
ca866c0d7d | ||
|
|
90a3f88c08 | ||
|
|
c1f89c5640 | ||
|
|
5865a78c1e | ||
|
|
d4616a7fc6 | ||
|
|
d794357504 | ||
|
|
79b0484e20 | ||
|
|
62b4741b19 | ||
|
|
327f06bbaf | ||
|
|
d7cd90b5b8 | ||
|
|
85fef90c94 | ||
|
|
fca25479ad | ||
|
|
231d965bcb | ||
|
|
6c10282f9a | ||
|
|
cb399272fd | ||
|
|
245bce04d1 | ||
|
|
3101bdf127 | ||
|
|
384ae9c407 | ||
|
|
c671f8e7ef | ||
|
|
f39b9c85fe | ||
|
|
ab36270885 | ||
|
|
bd65dd8098 | ||
|
|
36b6f308e6 | ||
|
|
cd208314e0 | ||
|
|
da7b5135de | ||
|
|
cb3d2b327a | ||
|
|
48db3a3e92 | ||
|
|
7182fb8fb0 | ||
|
|
d7da390ae9 | ||
|
|
54c1025ec0 | ||
|
|
5e7b7cba31 | ||
|
|
5a6b1cbf66 | ||
|
|
180e6d2c75 | ||
|
|
acbf25f2eb | ||
|
|
7c90cc6aac | ||
|
|
261af8dc07 | ||
|
|
2f344268ff | ||
|
|
42f3c24fc4 | ||
|
|
c4b97a1710 | ||
|
|
4f76bc42d7 | ||
|
|
fbd5600563 | ||
|
|
3259e2eac9 | ||
|
|
baca292f47 | ||
|
|
8babf0ec73 | ||
|
|
c07d124d4b | ||
|
|
a0db1eaa32 | ||
|
|
dbd61d129d | ||
|
|
21b5fb5ddb | ||
|
|
f55fafbc66 | ||
|
|
768843f1f0 | ||
|
|
cc7d1226ee | ||
|
|
8abafc54a6 | ||
|
|
885695fa67 | ||
|
|
e0bf309bb5 | ||
|
|
d907bcd57a | ||
|
|
0167bbb89a | ||
|
|
0471aaec52 | ||
|
|
18636a4df5 | ||
|
|
2be122875a | ||
|
|
c517f6daf9 | ||
|
|
07223bf5ef | ||
|
|
7862ba583e | ||
|
|
8c10d91135 | ||
|
|
c81d0f3393 | ||
|
|
0b8b9e8033 | ||
|
|
b73198d5ef | ||
|
|
4236699052 | ||
|
|
75935ad251 | ||
|
|
a3c3f47e17 | ||
|
|
17f07a36ef | ||
|
|
557a0c7ae3 | ||
|
|
1b98df4a0d | ||
|
|
58bfb0458e | ||
|
|
95cbf95c50 | ||
|
|
0b4402ebc8 | ||
|
|
d64b3f21e6 | ||
|
|
931fcfd0f5 | ||
|
|
a6ca9aa306 | ||
|
|
40ef282982 | ||
|
|
9152d6671c | ||
|
|
d057e95ce3 | ||
|
|
ac2932bac3 | ||
|
|
118331207b | ||
|
|
7c104c8fbb | ||
|
|
11c94b6566 | ||
|
|
c555b532a2 | ||
|
|
fdc4c25e67 | ||
|
|
083134bc12 | ||
|
|
f1c4a9ebfa | ||
|
|
1edd2ed3db | ||
|
|
b8b04271b5 | ||
|
|
f0b33f5ced | ||
|
|
42eead8f41 | ||
|
|
33054a18db | ||
|
|
f4036e8352 | ||
|
|
f9c30017b0 | ||
|
|
8be76ed450 | ||
|
|
328b25d561 | ||
|
|
9eff9676e9 | ||
|
|
a1d8335a8c | ||
|
|
d5a174364a | ||
|
|
62c3de30b9 | ||
|
|
ebcb46cd48 | ||
|
|
1689361ff2 | ||
|
|
05e177e2c8 | ||
|
|
eb998bda62 | ||
|
|
116bcb9dfb | ||
|
|
6a452951f5 | ||
|
|
a891854ef5 | ||
|
|
a2dffd430b | ||
|
|
4ac0580aa0 | ||
|
|
5d872b26a1 | ||
|
|
8d2c8065eb | ||
|
|
bf176ed712 | ||
|
|
eb875de67e | ||
|
|
2a77b9fcc7 | ||
|
|
9a4a5b1816 | ||
|
|
3a0ab861c2 | ||
|
|
fdc0d30337 | ||
|
|
96e4ec4a15 | ||
|
|
fa4b711495 | ||
|
|
0113b735ad | ||
|
|
d228ff0f43 | ||
|
|
9abbb581c5 | ||
|
|
d7c50f56d7 | ||
|
|
b6750de9e8 | ||
|
|
c91caa06d3 | ||
|
|
6c3c184bc7 | ||
|
|
af71292aba | ||
|
|
24e29cecae | ||
|
|
ab4c3a326f | ||
|
|
d73ca29a07 | ||
|
|
d6fbe45296 | ||
|
|
dd1cd5e491 | ||
|
|
2b02db2675 | ||
|
|
6f93a8f091 | ||
|
|
47074c897e | ||
|
|
2270f0d172 | ||
|
|
1b5d3466ba | ||
|
|
dcc2247a90 | ||
|
|
00b7aeb49b | ||
|
|
4f5b781b88 | ||
|
|
b51d1b79a3 | ||
|
|
4848de3a8e | ||
|
|
88b1cebe1d | ||
|
|
bb9e9e5480 | ||
|
|
786a7fbaf4 | ||
|
|
0ce27296aa | ||
|
|
a1d176b643 | ||
|
|
0c18e653e8 | ||
|
|
065b8c945b | ||
|
|
f49fc35f55 | ||
|
|
67795ad26e | ||
|
|
2aef8bad55 | ||
|
|
9f05cabf87 | ||
|
|
4c07b1606c | ||
|
|
d3f0a3211d | ||
|
|
ec7333d406 | ||
|
|
6a40426421 | ||
|
|
c9ebb895a1 | ||
|
|
f66bbda8b1 | ||
|
|
da2d9f3733 | ||
|
|
9e0252b209 | ||
|
|
531ea17088 | ||
|
|
e89f5983be | ||
|
|
6784fd64f3 | ||
|
|
ced2543be0 | ||
|
|
261e92c57b | ||
|
|
55bd48dc10 | ||
|
|
96dc30dea2 | ||
|
|
ea7b0052f3 | ||
|
|
1db9013bc4 | ||
|
|
060cbe15d6 | ||
|
|
8532d0d152 | ||
|
|
173e63f01b | ||
|
|
066daf21ef | ||
|
|
10f4604939 | ||
|
|
08ab2d89f4 | ||
|
|
775deefeaa | ||
|
|
cbf281589b | ||
|
|
f1d95379bc | ||
|
|
7c4377af0e | ||
|
|
f93b8fe377 | ||
|
|
978c68dcf8 | ||
|
|
c0925d2d04 | ||
|
|
15e8208457 | ||
|
|
d8fe18c28e | ||
|
|
9ee85fb1b6 | ||
|
|
bdbc96f453 | ||
|
|
5f7831b3c8 | ||
|
|
6336279ce0 | ||
|
|
ab4b59afb9 | ||
|
|
046a270156 | ||
|
|
2f293d53ba | ||
|
|
1d7caed340 | ||
|
|
5993c816d8 | ||
|
|
15ccac78d9 | ||
|
|
d81841f54e | ||
|
|
95f4c02381 | ||
|
|
df0e4492a7 | ||
|
|
e01f342048 | ||
|
|
792f5b3f22 | ||
|
|
a42baedc28 | ||
|
|
6607cab902 | ||
|
|
3a09de82e3 | ||
|
|
c028619cdd | ||
|
|
6b1dc21383 | ||
|
|
613acd93c0 | ||
|
|
837cb42679 | ||
|
|
0e9cb48ea1 | ||
|
|
090aaefb39 | ||
|
|
0b9c9f9ede | ||
|
|
8e65fb920f | ||
|
|
945460c67e | ||
|
|
b7e4d2a8ff | ||
|
|
9768657fc7 | ||
|
|
a185e19bc1 | ||
|
|
b7603f8600 | ||
|
|
d8d281281c | ||
|
|
5ee9c95ebd | ||
|
|
fb5a1a2c0f | ||
|
|
4a5a7ee922 | ||
|
|
d258bc263a | ||
|
|
8a30754e9c | ||
|
|
a14bc9ed44 | ||
|
|
75adf4bac6 | ||
|
|
8d6013454c | ||
|
|
1a25062af0 | ||
|
|
8aedcadd3e | ||
|
|
027d59fd46 | ||
|
|
273e70e403 | ||
|
|
aee6c1f72f | ||
|
|
9ee40d9f88 | ||
|
|
754fe60979 | ||
|
|
3a429eabcd | ||
|
|
9209a2ee33 | ||
|
|
3a607a197e | ||
|
|
3639daa329 | ||
|
|
8fd6a61df6 | ||
|
|
a6b0d40f9d | ||
|
|
974cf08e5d | ||
|
|
75fd6fc4a4 | ||
|
|
4f7dc3c76c | ||
|
|
7abfe74fa4 | ||
|
|
d5a159d7af | ||
|
|
add68e0da1 | ||
|
|
a85f9db5b1 | ||
|
|
c43ab76bbe | ||
|
|
98df3c6e0a | ||
|
|
0a4130a4ff | ||
|
|
93828fde80 | ||
|
|
a3fdc33f88 | ||
|
|
a330917ee8 | ||
|
|
0fe5dd8d43 | ||
|
|
953d783a3e | ||
|
|
d1ef610822 | ||
|
|
2ff620e1b5 | ||
|
|
0aaf69769f | ||
|
|
b5ab0c5285 | ||
|
|
2442b985bf | ||
|
|
78201c0146 | ||
|
|
499f4689be | ||
|
|
2106f491d0 | ||
|
|
81ac03a28b | ||
|
|
980af63b8d | ||
|
|
613644bfc6 | ||
|
|
7f5d6bc964 | ||
|
|
d5e37309e5 | ||
|
|
6bfa99a774 | ||
|
|
c899097802 | ||
|
|
70187bd47e | ||
|
|
018efc9631 | ||
|
|
a4f4e6e71d | ||
|
|
cde11f9d9c | ||
|
|
8fa3b6802e | ||
|
|
4192df97ec | ||
|
|
9085abd838 | ||
|
|
874415a193 | ||
|
|
5e9d80fbdb | ||
|
|
75f7f21eb0 | ||
|
|
c01e265e07 | ||
|
|
5c89228878 | ||
|
|
2740a81103 | ||
|
|
a27dc1e908 | ||
|
|
e9abfa6602 | ||
|
|
0f8a2d6418 | ||
|
|
2e479b88a8 | ||
|
|
fd982597fe | ||
|
|
887077b5a4 | ||
|
|
4e71ec4480 | ||
|
|
8ef712787a | ||
|
|
6c39b8a10c | ||
|
|
3c32c993f5 | ||
|
|
62d705903e | ||
|
|
a5e2e9ea03 | ||
|
|
055e3d52b6 | ||
|
|
efd3104fd0 | ||
|
|
bd570561e8 | ||
|
|
19d49db305 | ||
|
|
ab4993fda2 | ||
|
|
6f4056cf99 | ||
|
|
9cf1390dc6 | ||
|
|
6a2e323792 | ||
|
|
d4834803ae | ||
|
|
84fc489e0c | ||
|
|
255d5a748f | ||
|
|
b41520f9e2 | ||
|
|
0e881170c2 | ||
|
|
1571881f7a | ||
|
|
4dbf1dcad4 | ||
|
|
824ef1ff2c | ||
|
|
79d9b36ece | ||
|
|
be316e81a7 | ||
|
|
615e52d396 | ||
|
|
7f232b43ff | ||
|
|
5a12aeb4a1 | ||
|
|
05ea5f05ae | ||
|
|
e895aa71a4 | ||
|
|
eb28133b1f | ||
|
|
c66932ab25 | ||
|
|
36e8213e42 | ||
|
|
cde6d0deed | ||
|
|
b462cb4575 | ||
|
|
dd4ccfc672 | ||
|
|
6006e2f917 | ||
|
|
4bedb07aa0 | ||
|
|
02e194b15f | ||
|
|
82e579a29d | ||
|
|
eacac8ee1d | ||
|
|
cd19d81411 | ||
|
|
748ba22a19 | ||
|
|
79be888b6f | ||
|
|
58b3b0603d | ||
|
|
9d43d74ee3 | ||
|
|
9bc49b3268 | ||
|
|
d92e0dec22 | ||
|
|
7932bf483b | ||
|
|
e54f3fdaac | ||
|
|
e8b055b8d3 | ||
|
|
0c94f22ded | ||
|
|
05909d6fa7 | ||
|
|
712a22e18c | ||
|
|
35893e36dd | ||
|
|
787eecbf43 | ||
|
|
5f1aac1450 | ||
|
|
7fdd312cca | ||
|
|
f16911f7dd | ||
|
|
f6bdd2cae4 | ||
|
|
f8aa14d402 | ||
|
|
11a4f5538b | ||
|
|
26e88dc2c1 | ||
|
|
34fcccc50b | ||
|
|
2f53df93e8 | ||
|
|
996d5fda69 | ||
|
|
f75be85eea | ||
|
|
f1412f5e5a | ||
|
|
4208bcd8d1 | ||
|
|
5368a760cf | ||
|
|
9ec08fa76a | ||
|
|
913a99dd35 | ||
|
|
d956a39fec | ||
|
|
01d9bd573e | ||
|
|
d301b69dae | ||
|
|
c1fe7517ec | ||
|
|
cd1c96db56 | ||
|
|
4aee4c1ac7 | ||
|
|
bef0503545 | ||
|
|
7aea2cd6f1 | ||
|
|
028d707077 | ||
|
|
c9df03d1a9 | ||
|
|
19338d2774 | ||
|
|
005e5f2f72 | ||
|
|
e54cd23ecb | ||
|
|
544e469eeb | ||
|
|
db739d16ce | ||
|
|
5fff6e7ff8 | ||
|
|
776c01e60b | ||
|
|
3eb38d7611 | ||
|
|
766159e27e | ||
|
|
bb34d8cfd1 | ||
|
|
a880469d7d | ||
|
|
e5147d5562 | ||
|
|
d7ff85850c | ||
|
|
7294cb080b | ||
|
|
b422864d5d | ||
|
|
eab3667b61 | ||
|
|
713e89cdb2 | ||
|
|
b290f0442d | ||
|
|
b035a6196a | ||
|
|
e4cd00b5ea | ||
|
|
3bb13829fe | ||
|
|
1af676385c | ||
|
|
a24a170b84 | ||
|
|
bc4b3764fc | ||
|
|
9b143dd5dc | ||
|
|
5c65d4733b | ||
|
|
41a84ef1d3 | ||
|
|
86cecd6700 | ||
|
|
fdcb87db19 | ||
|
|
a868832776 | ||
|
|
5eabd4591a | ||
|
|
91afefa61a | ||
|
|
ea7bdb8e9e | ||
|
|
7cda728469 | ||
|
|
7fd37d4e87 | ||
|
|
874bfe19d2 | ||
|
|
ed07d481d6 | ||
|
|
7115975212 | ||
|
|
ddf0982d0f | ||
|
|
7b54ddd828 | ||
|
|
1ffd049833 | ||
|
|
12e0a2ef7d | ||
|
|
9da7afb345 | ||
|
|
1f544e087b | ||
|
|
e1b6a4cb21 | ||
|
|
bb8522b737 | ||
|
|
e075581d76 | ||
|
|
527d18a526 | ||
|
|
5ace5976a1 | ||
|
|
c52ac01fbc | ||
|
|
f4f2bfe379 | ||
|
|
75beb3bd35 | ||
|
|
1d22294974 | ||
|
|
de665f7cf9 | ||
|
|
205520c620 | ||
|
|
3551b2a2a1 | ||
|
|
11f2e8d1a6 | ||
|
|
a91374a916 | ||
|
|
36e6c26545 | ||
|
|
9a68f74857 | ||
|
|
b96c15890c | ||
|
|
5a6b26d69e | ||
|
|
a3d912a801 | ||
|
|
b4f1deaa46 | ||
|
|
976bce4c7f | ||
|
|
77e0f77850 | ||
|
|
a885cbf8ed | ||
|
|
b6af5d9bda | ||
|
|
b72677c3ce | ||
|
|
5e44122552 | ||
|
|
d4b6471d39 | ||
|
|
fe1bea9538 | ||
|
|
2f41e2b0ec | ||
|
|
bd86f95512 | ||
|
|
5fcf6418cf | ||
|
|
4e583dab2e | ||
|
|
bc182cab7c | ||
|
|
f6f6f11838 | ||
|
|
b5f4672155 | ||
|
|
729da3c873 | ||
|
|
8a31c6f807 | ||
|
|
2539b5cf61 | ||
|
|
f7881e58bb | ||
|
|
25ed4e9d16 | ||
|
|
7eddf93b36 | ||
|
|
cfb082a20b | ||
|
|
e440590c37 | ||
|
|
c851a19ab2 | ||
|
|
c2f9d7dee0 | ||
|
|
473e567047 | ||
|
|
64dab92c6b | ||
|
|
f576755b13 | ||
|
|
d0f279ece4 | ||
|
|
a5532ffd4d | ||
|
|
cb412134dd | ||
|
|
d079aae0e0 | ||
|
|
eabfb233b8 | ||
|
|
960e724ad3 | ||
|
|
82c46f957a | ||
|
|
320ea29861 | ||
|
|
c5e99ada63 | ||
|
|
c5bd3f5e3b | ||
|
|
a88d054d05 | ||
|
|
6576802287 | ||
|
|
693316c83f | ||
|
|
41ff80e5d3 | ||
|
|
56c5fdbeaf | ||
|
|
130d39cc03 | ||
|
|
1485edf8e1 | ||
|
|
514ee867a6 | ||
|
|
3cc7a406f4 | ||
|
|
c3f82acf7a | ||
|
|
cdb7557d88 | ||
|
|
2dcbd016e6 | ||
|
|
7d1728f05a | ||
|
|
4b5c14470a | ||
|
|
f198b11f27 | ||
|
|
7d16087789 | ||
|
|
d734cd986c | ||
|
|
8559f3e583 | ||
|
|
d0b81d3b20 | ||
|
|
43bf9a8e46 | ||
|
|
19f57bf810 | ||
|
|
469012b544 | ||
|
|
9b11c4670d | ||
|
|
7eeaf8eaa5 | ||
|
|
a82e02ef15 | ||
|
|
d8922d0410 | ||
|
|
5c628d2a61 | ||
|
|
54bb20b486 | ||
|
|
57a6eaae7b | ||
|
|
7a5cb21056 | ||
|
|
333c3f18c0 | ||
|
|
6b2979027e | ||
|
|
12f9adbdd3 | ||
|
|
a5cab96ff3 | ||
|
|
dd060ceed3 | ||
|
|
c89b085e5b | ||
|
|
63140ca7f8 | ||
|
|
cbe83f32b6 | ||
|
|
3bffb0a181 | ||
|
|
09dc9c89a8 | ||
|
|
09707a3e81 | ||
|
|
d821277430 | ||
|
|
1f01634029 | ||
|
|
dd314e1bdf | ||
|
|
1dae102359 | ||
|
|
b306594cad | ||
|
|
b60b7cf8fe | ||
|
|
0148128881 | ||
|
|
076a884651 | ||
|
|
7026bbcdd4 | ||
|
|
6158aa684b | ||
|
|
047e3455f7 | ||
|
|
9ca9649c1b | ||
|
|
9a23e9839b | ||
|
|
7fc966dced | ||
|
|
680afa4229 | ||
|
|
313743c833 | ||
|
|
6cb19d7e34 | ||
|
|
ef0575a26d | ||
|
|
c9db715bae | ||
|
|
ed1e3899af | ||
|
|
7446e31fce | ||
|
|
94abf9d935 | ||
|
|
d9b4ab1658 | ||
|
|
39f8538776 | ||
|
|
fef5c4c9a0 | ||
|
|
61dc2e7dd5 | ||
|
|
11a820458f | ||
|
|
9e7418090d | ||
|
|
0b3b713123 | ||
|
|
00f8cf57cd | ||
|
|
c55c375b0a | ||
|
|
db76ca248b | ||
|
|
182e1909b2 | ||
|
|
3aea7bc053 | ||
|
|
39c35732de | ||
|
|
6c8850698d | ||
|
|
818a726679 | ||
|
|
f8663a7129 | ||
|
|
7571fe5d13 | ||
|
|
60dc1c22f7 | ||
|
|
02b575e9e4 | ||
|
|
6facaf8f05 | ||
|
|
319a972e75 | ||
|
|
61cdbb30a6 | ||
|
|
5088a38b9f | ||
|
|
f5c6a6d075 | ||
|
|
b5340dd739 | ||
|
|
e1dcfcbb76 | ||
|
|
43d448e73f | ||
|
|
33fbb06604 | ||
|
|
e1b215aa73 | ||
|
|
6a17ef925d | ||
|
|
7599985a0a | ||
|
|
07c0bc288a | ||
|
|
f05d392920 | ||
|
|
e11d57c883 | ||
|
|
baf377679a | ||
|
|
f9355136d8 | ||
|
|
bb76e69567 | ||
|
|
533fc217d4 | ||
|
|
ac5c6695c1 | ||
|
|
3160488352 | ||
|
|
8579e4144f | ||
|
|
6880fa62f8 | ||
|
|
2cf4287197 | ||
|
|
86c774443c | ||
|
|
3b715f4602 | ||
|
|
ec0c2bea6e | ||
|
|
1b3682cfa9 | ||
|
|
2a9ee35411 | ||
|
|
0e0a7d82de | ||
|
|
dfa96852f0 | ||
|
|
a549ec0382 | ||
|
|
c61f01b206 | ||
|
|
15257f4524 | ||
|
|
089378ef87 | ||
|
|
805f495d84 | ||
|
|
928dd94ba6 | ||
|
|
b429918b32 | ||
|
|
62a37a4876 | ||
|
|
0e96ece6c6 | ||
|
|
f1eca32536 | ||
|
|
ed59dc1ada | ||
|
|
a5cd3fcafd | ||
|
|
f3def542ee | ||
|
|
aa249922df | ||
|
|
37a0c57bed | ||
|
|
90b7e68b4e | ||
|
|
1a0c497007 | ||
|
|
fd49ee3c8d | ||
|
|
3488a5c3c4 | ||
|
|
ae4955f59e | ||
|
|
fab107a9df | ||
|
|
bb87d19e84 | ||
|
|
4e309b7992 | ||
|
|
64aa4c716d | ||
|
|
6b0852a21f | ||
|
|
8afd3e882e | ||
|
|
791fa28817 | ||
|
|
3fab752640 | ||
|
|
67dd939d83 | ||
|
|
ec9b447be1 | ||
|
|
99e3fe9ae1 | ||
|
|
eb99d21022 | ||
|
|
793cbcdff8 | ||
|
|
751557bcbf | ||
|
|
252dcd8626 | ||
|
|
0ad7ea16d2 | ||
|
|
79842d4fdd | ||
|
|
1e1f5d5462 | ||
|
|
19d8ffe177 | ||
|
|
4cb8192c1d | ||
|
|
2ed5bc5195 | ||
|
|
fae1397468 | ||
|
|
e871548772 | ||
|
|
a3d27e30e0 | ||
|
|
89abd1f58e | ||
|
|
fae709d9ea | ||
|
|
36df79b207 | ||
|
|
b9ebcd7738 | ||
|
|
515fb5a3fd | ||
|
|
785f90d9fa | ||
|
|
22ac311496 | ||
|
|
0e31ca73c0 | ||
|
|
98ba558fa5 | ||
|
|
c4ef1e5c19 | ||
|
|
1fa0d66f15 | ||
|
|
4680307a34 | ||
|
|
42a6fa972d | ||
|
|
1da6c35a6d | ||
|
|
7c427a4dce | ||
|
|
c2abae538b | ||
|
|
eb69032773 | ||
|
|
fb5452910f | ||
|
|
a6d37fcb25 | ||
|
|
fb18e1f036 | ||
|
|
786cbee510 | ||
|
|
a4f5673ff3 | ||
|
|
1121f2b7f7 | ||
|
|
4d30218eb2 | ||
|
|
d7699ca81d | ||
|
|
afc3bc6b65 | ||
|
|
6f066fca82 | ||
|
|
5e24ae49a6 | ||
|
|
bf74ffe974 | ||
|
|
2074861b57 | ||
|
|
58b64d42f1 | ||
|
|
a6ab0405ea | ||
|
|
8273b54108 | ||
|
|
c356f560e8 | ||
|
|
8d052ecf24 | ||
|
|
45e36d9f56 | ||
|
|
c222d8b672 | ||
|
|
efed3f1f0d | ||
|
|
7524893802 | ||
|
|
25a0273b05 | ||
|
|
ce0410b205 | ||
|
|
efe5ce5c78 | ||
|
|
07f52b4b96 | ||
|
|
0be3598990 | ||
|
|
c3284c30cd | ||
|
|
2d94bf4998 | ||
|
|
31c1e72b8c | ||
|
|
d11fcafad8 | ||
|
|
27c6d299cf | ||
|
|
450dd65165 | ||
|
|
26819e6c39 | ||
|
|
6587efb3e8 | ||
|
|
8d2200f29b | ||
|
|
e4295396ac | ||
|
|
5c7a70df28 | ||
|
|
16a7a8258f | ||
|
|
fb1f61b212 | ||
|
|
c3137df39c | ||
|
|
2df9cd7262 | ||
|
|
a23f6afbee | ||
|
|
e37f7fafc1 | ||
|
|
7b803465ad | ||
|
|
273612b251 | ||
|
|
e2bc8e86e2 | ||
|
|
79110ba469 | ||
|
|
9dd10e46ab | ||
|
|
29df95c514 | ||
|
|
015cb1a35d | ||
|
|
b217b020a5 | ||
|
|
0c960a82ce | ||
|
|
e84cc8c0ad | ||
|
|
c69b93b246 | ||
|
|
5e13b9528d | ||
|
|
8bb3cab9d2 | ||
|
|
ed201c35d2 | ||
|
|
4eadc22a36 | ||
|
|
4e2260c74c | ||
|
|
a5148c14a1 | ||
|
|
955d6206ee | ||
|
|
5df22b3468 | ||
|
|
aef86ec5ef | ||
|
|
94d3b04d68 | ||
|
|
d31830225c | ||
|
|
8f309a3fec | ||
|
|
1fe4edbe1c | ||
|
|
fe1696cef7 | ||
|
|
de2e311e6e | ||
|
|
384fb3c353 | ||
|
|
8603e19516 | ||
|
|
7a2751043a | ||
|
|
e2b0c6c702 | ||
|
|
5b12c61a81 | ||
|
|
b8ee8808b4 | ||
|
|
2db9551750 | ||
|
|
7dc51270ee | ||
|
|
237b5a2467 | ||
|
|
110e5af36c | ||
|
|
7d4aef9576 | ||
|
|
811b360df7 | ||
|
|
d91b54882a | ||
|
|
9106a1f2d5 | ||
|
|
2e2dada561 | ||
|
|
6d704c728e | ||
|
|
6c6f32f17f | ||
|
|
0e4ce326c2 | ||
|
|
d27448da8a | ||
|
|
d36b723056 | ||
|
|
2c8aa6f6f4 | ||
|
|
6f7c8fb355 | ||
|
|
82f852abf3 | ||
|
|
239c6fe070 | ||
|
|
579d2ad811 | ||
|
|
0cb355cd7c | ||
|
|
86135d12b9 | ||
|
|
10d42c6fa3 | ||
|
|
6434f7efad | ||
|
|
bfdf19f56c | ||
|
|
79c7a7a43d | ||
|
|
175053085a | ||
|
|
3d90c062fd | ||
|
|
fda71b1230 | ||
|
|
a916de3b66 | ||
|
|
30e5dfddb9 | ||
|
|
f9dea9d35b | ||
|
|
4ea55b1bbc | ||
|
|
caf3c16826 | ||
|
|
a6f1111469 | ||
|
|
33d0096b81 | ||
|
|
ed4c5d88a3 | ||
|
|
e85a892111 | ||
|
|
70cd10beae | ||
|
|
3a784509df | ||
|
|
5c7189124c | ||
|
|
f81c2744cc | ||
|
|
4bf3359574 | ||
|
|
47984b0496 | ||
|
|
3c339323ed | ||
|
|
7399722a88 | ||
|
|
7bfea6edb7 | ||
|
|
f75da2c393 | ||
|
|
475576ec20 | ||
|
|
f6a8e1df03 | ||
|
|
e1c4bfad05 | ||
|
|
1ed40e7983 | ||
|
|
035fcce9de | ||
|
|
19eed5ff8a | ||
|
|
7129d4797c | ||
|
|
707d31a083 | ||
|
|
79e66ac841 | ||
|
|
1b96391cdf | ||
|
|
eed8ea5bc1 | ||
|
|
a646b5374f | ||
|
|
fa9b1ba947 | ||
|
|
7d58c0c2be | ||
|
|
3c71c378bc | ||
|
|
e4a6d199fe | ||
|
|
bdfdc2bb83 | ||
|
|
351e590e01 | ||
|
|
45756246b9 |
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# These files are text and should be normalized
|
||||
*.txt text
|
||||
*.c text
|
||||
*.h text
|
||||
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
/doc/doxygen/output/html
|
||||
/src/apps/snmp/LwipMibCompiler/CCodeGeneration/bin/
|
||||
/src/apps/snmp/LwipMibCompiler/CCodeGeneration/obj/
|
||||
/src/apps/snmp/LwipMibCompiler/LwipMibCompiler/bin/
|
||||
/src/apps/snmp/LwipMibCompiler/LwipMibCompiler/obj/
|
||||
/src/apps/snmp/LwipMibCompiler/MibViewer/bin/
|
||||
/src/apps/snmp/LwipMibCompiler/MibViewer/obj/
|
||||
/src/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/bin/
|
||||
/src/apps/snmp/LwipMibCompiler/LwipSnmpCodeGeneration/obj/
|
||||
/src/apps/snmp/LwipMibCompiler/SharpSnmpLib/bin/
|
||||
/src/apps/snmp/LwipMibCompiler/SharpSnmpLib/obj/
|
||||
/src/apps/snmp/LwipMibCompiler/LwipMibCompiler.userprefs
|
||||
/src/apps/snmp/LwipMibCompiler/*.suo
|
||||
1
FILES
1
FILES
@@ -1,4 +1,5 @@
|
||||
src/ - The source code for the lwIP TCP/IP stack.
|
||||
doc/ - The documentation for lwIP.
|
||||
test/ - Some code to test whether the sources do what they should.
|
||||
|
||||
See also the FILES file in each subdirectory.
|
||||
|
||||
104
README
104
README
@@ -7,26 +7,44 @@ Science (SICS).
|
||||
|
||||
The focus of the lwIP TCP/IP implementation is to reduce the RAM usage
|
||||
while still having a full scale TCP. This making lwIP suitable for use
|
||||
in embedded systems with tenths of kilobytes of free RAM and room for
|
||||
in embedded systems with tens of kilobytes of free RAM and room for
|
||||
around 40 kilobytes of code ROM.
|
||||
|
||||
|
||||
FEATURES
|
||||
|
||||
* IP (Internet Protocol) including packet forwarding over multiple
|
||||
network interfaces
|
||||
* ICMP (Internet Control Message Protocol) for network maintenance
|
||||
and debugging
|
||||
* UDP (User Datagram Protocol) including experimental UDP-lite
|
||||
extensions
|
||||
* TCP (Transmission Control Protocol) with congestion control, RTT
|
||||
estimation and fast recovery/fast retransmit
|
||||
* Specialized API for enhanced performance
|
||||
* Optional Berkeley socket API
|
||||
* IP (Internet Protocol, IPv4 and IPv6) including packet forwarding over
|
||||
multiple network interfaces
|
||||
* ICMP (Internet Control Message Protocol) for network maintenance and debugging
|
||||
* IGMP (Internet Group Management Protocol) for multicast traffic management
|
||||
* MLD (Multicast listener discovery for IPv6). Aims to be compliant with
|
||||
RFC 2710. No support for MLDv2
|
||||
* ND (Neighbor discovery and stateless address autoconfiguration for IPv6).
|
||||
Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
|
||||
(Address autoconfiguration)
|
||||
* UDP (User Datagram Protocol) including experimental UDP-lite extensions
|
||||
* TCP (Transmission Control Protocol) with congestion control, RTT estimation
|
||||
and fast recovery/fast retransmit
|
||||
* raw/native API for enhanced performance
|
||||
* Optional Berkeley-like socket API
|
||||
* DNS (Domain names resolver)
|
||||
|
||||
|
||||
APPLICATIONS
|
||||
|
||||
* HTTP server with SSI and CGI
|
||||
* SNMPv2c agent with MIB compiler (Simple Network Management Protocol)
|
||||
* SNTP (Simple network time protocol)
|
||||
* NetBIOS name service responder
|
||||
* MDNS (Multicast DNS) responder
|
||||
* iPerf server implementation
|
||||
|
||||
|
||||
LICENSE
|
||||
|
||||
lwIP is freely available under a BSD license.
|
||||
|
||||
|
||||
DEVELOPMENT
|
||||
|
||||
lwIP has grown into an excellent TCP/IP stack for embedded devices,
|
||||
@@ -35,58 +53,48 @@ and additions to the stack to further increase its usefulness.
|
||||
|
||||
Development of lwIP is hosted on Savannah, a central point for
|
||||
software development, maintenance and distribution. Everyone can
|
||||
help improve lwIP by use of Savannah's interface, CVS and the
|
||||
help improve lwIP by use of Savannah's interface, Git and the
|
||||
mailing list. A core team of developers will commit changes to the
|
||||
CVS source tree.
|
||||
Git source tree.
|
||||
|
||||
The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and
|
||||
contributions (such as platform ports) are in the 'contrib' module.
|
||||
The lwIP TCP/IP stack is maintained in the 'lwip' Git module and
|
||||
contributions (such as platform ports) are in the 'contrib' Git module.
|
||||
|
||||
The CVS main trunk is the stable branch, which contains bug fixes and
|
||||
tested features. The latest stable branch can be checked out by doing:
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip login
|
||||
cvs -z3 -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip co lwip
|
||||
See doc/savannah.txt for details on Git server access for users and
|
||||
developers.
|
||||
|
||||
The 'STABLE' tag in the stable branch will represent the most stable
|
||||
revision (which may be somewhat older to protect us from errors
|
||||
introduced by merges). This 'STABLE' tagged version can be checked out
|
||||
by doing:
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip login
|
||||
cvs -z3 -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip co -r STABLE lwip
|
||||
|
||||
The 'DEVEL' branch is the active development branch, which contains
|
||||
bleeding edge changes, and may be instable. It can be checkout by doing:
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip login
|
||||
cvs -z3 -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip co -r DEVEL lwip
|
||||
|
||||
The current contrib CVS tree can be checked out by doing:
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip login
|
||||
cvs -z3 -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip co contrib
|
||||
|
||||
Last night's CVS tar ball can be downloaded from:
|
||||
http://savannah.gnu.org/cvs.backups/lwip.tar.gz
|
||||
|
||||
The current CVS trees are web-browsable:
|
||||
http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/
|
||||
http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/
|
||||
The current Git trees are web-browsable:
|
||||
http://git.savannah.gnu.org/cgit/lwip.git
|
||||
http://git.savannah.gnu.org/cgit/lwip/lwip-contrib.git
|
||||
|
||||
Submit patches and bugs via the lwIP project page:
|
||||
http://savannah.nongnu.org/projects/lwip/
|
||||
|
||||
Continuous integration builds (GCC, clang):
|
||||
https://travis-ci.org/yarrick/lwip-merged
|
||||
|
||||
|
||||
DOCUMENTATION
|
||||
|
||||
The original out-dated homepage of lwIP and Adam Dunkels' papers on
|
||||
lwIP are at the official lwIP home page:
|
||||
http://www.sics.se/~adam/lwip/
|
||||
|
||||
Self documentation of the source code is regularly extracted from the
|
||||
current CVS sources and is available from this web page:
|
||||
Self documentation of the source code is regularly extracted from the current
|
||||
Git sources and is available from this web page:
|
||||
http://www.nongnu.org/lwip/
|
||||
|
||||
There is now a constantly growing wiki about lwIP at
|
||||
http://lwip.wikia.com/wiki/LwIP_Wiki
|
||||
|
||||
Also, there are mailing lists you can subscribe at
|
||||
http://savannah.nongnu.org/mail/?group=lwip
|
||||
plus searchable archives:
|
||||
http://lists.nongnu.org/archive/html/lwip-users/
|
||||
http://lists.nongnu.org/archive/html/lwip-devel/
|
||||
|
||||
lwIP was originally written by Adam Dunkels:
|
||||
http://dunkels.com/adam/
|
||||
|
||||
Reading Adam's papers, the files in docs/, browsing the source code
|
||||
documentation and browsing the mailing list archives is a good way to
|
||||
become familiar with the design of lwIP.
|
||||
|
||||
Adam Dunkels <adam@sics.se>
|
||||
Leon Woestenberg <leon.woestenberg@gmx.net>
|
||||
Leon Woestenberg <leon.woestenberg@gmx.net>
|
||||
|
||||
228
UPGRADING
Normal file
228
UPGRADING
Normal file
@@ -0,0 +1,228 @@
|
||||
This file lists major changes between release versions that require
|
||||
ports or applications to be changed. Use it to update a port or an
|
||||
application written for an older version of lwIP to correctly work
|
||||
with newer versions.
|
||||
|
||||
|
||||
(git master)
|
||||
|
||||
* [Enter new changes just after this line - do not remove this line]
|
||||
|
||||
* TODO
|
||||
|
||||
(2.0.0)
|
||||
|
||||
++ Application changes:
|
||||
|
||||
* Changed netif "up" flag handling to be an administrative flag (as opposed to the previous meaning of
|
||||
"ip4-address-valid", a netif will now not be used for transmission if not up) -> even a DHCP netif
|
||||
has to be set "up" before starting the DHCP client
|
||||
* Added IPv6 support (dual-stack or IPv4/IPv6 only)
|
||||
* Changed ip_addr_t to be a union in dual-stack mode (use ip4_addr_t where referring to IPv4 only).
|
||||
* Major rewrite of SNMP (added MIB parser that creates code stubs for custom MIBs);
|
||||
supports SNMPv2c (experimental v3 support)
|
||||
* Moved some core applications from contrib repository to src/apps (and include/lwip/apps)
|
||||
|
||||
+++ Raw API:
|
||||
* Changed TCP listen backlog: removed tcp_accepted(), added the function pair tcp_backlog_delayed()/
|
||||
tcp_backlog_accepted() to explicitly delay backlog handling on a connection pcb
|
||||
|
||||
+++ Socket API:
|
||||
* Added an implementation for posix sendmsg()
|
||||
* Added LWIP_FIONREAD_LINUXMODE that makes ioctl/FIONREAD return the size of the next pending datagram
|
||||
|
||||
++ Port changes
|
||||
|
||||
+++ new files:
|
||||
* MANY new and moved files!
|
||||
* Added src/Filelists.mk for use in Makefile projects
|
||||
* Continued moving stack-internal parts from abc.h to abc_priv.h in sub-folder "priv"
|
||||
to let abc.h only contain the actual application programmer's API
|
||||
|
||||
+++ sys layer:
|
||||
* Made LWIP_TCPIP_CORE_LOCKING==1 the default as it usually performs better than
|
||||
the traditional message passing (although with LWIP_COMPAT_MUTEX you are still
|
||||
open to priority inversion, so this is not recommended any more)
|
||||
* Added LWIP_NETCONN_SEM_PER_THREAD to use one "op_completed" semaphore per thread
|
||||
instead of using one per netconn (these semaphores are used even with core locking
|
||||
enabled as some longer lasting functions like big writes still need to delay)
|
||||
* Added generalized abstraction for itoa(), strnicmp(), stricmp() and strnstr()
|
||||
in def.h (to be overridden in cc.h) instead of config
|
||||
options for netbiosns, httpd, dns, etc. ...
|
||||
* New abstraction for hton* and ntoh* functions in def.h.
|
||||
To override them, use the following in cc.h:
|
||||
#define lwip_htons(x) <your_htons>
|
||||
#define lwip_htonl(x) <your_htonl>
|
||||
|
||||
+++ new options:
|
||||
* TODO
|
||||
|
||||
+++ new pools:
|
||||
* Added LWIP_MEMPOOL_* (declare/init/alloc/free) to declare private memp pools
|
||||
that share memp.c code but do not have to be made global via lwippools.h
|
||||
* Added pools for IPv6, MPU_COMPATIBLE, dns-api, netif-api, etc.
|
||||
* added hook LWIP_HOOK_MEMP_AVAILABLE() to get informed when a memp pool was empty and an item
|
||||
is now available
|
||||
|
||||
* Signature of LWIP_HOOK_VLAN_SET macro was changed
|
||||
|
||||
* LWIP_DECLARE_MEMORY_ALIGNED() may be used to declare aligned memory buffers (mem/memp)
|
||||
or to move buffers to dedicated memory using compiler attributes
|
||||
|
||||
* Standard C headers are used to define sized types and printf formatters
|
||||
(disable by setting LWIP_NO_STDINT_H=1 or LWIP_NO_INTTYPES_H=1 if your compiler
|
||||
does not support these)
|
||||
|
||||
|
||||
++ Major bugfixes/improvements
|
||||
|
||||
* Added IPv6 support (dual-stack or IPv4/IPv6 only)
|
||||
* Major rewrite of PPP (incl. keep-up with apache pppd)
|
||||
see doc/ppp.txt for an upgrading how-to
|
||||
* Major rewrite of SNMP (incl. MIB parser)
|
||||
* Fixed timing issues that might have lead to losing a DHCP lease
|
||||
* Made rx processing path more robust against crafted errors
|
||||
* TCP window scaling support
|
||||
* modification of api modules to support FreeRTOS-MPU (don't pass stack-pointers to other threads)
|
||||
* made DNS client more robust
|
||||
* support PBUF_REF for RX packets
|
||||
* LWIP_NETCONN_FULLDUPLEX allows netconn/sockets to be used for reading/writing from separate
|
||||
threads each (needs LWIP_NETCONN_SEM_PER_THREAD)
|
||||
* Moved and reordered stats (mainly memp/mib2)
|
||||
|
||||
(1.4.0)
|
||||
|
||||
++ Application changes:
|
||||
|
||||
* Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for
|
||||
compatibility to old applications, but will be removed in the future).
|
||||
|
||||
* Renamed mem_realloc() to mem_trim() to prevent confusion with realloc()
|
||||
|
||||
+++ Raw API:
|
||||
* Changed the semantics of tcp_close() (since it was rather a
|
||||
shutdown before): Now the application does *NOT* get any calls to the recv
|
||||
callback (aside from NULL/closed) after calling tcp_close()
|
||||
|
||||
* When calling tcp_abort() from a raw API TCP callback function,
|
||||
make sure you return ERR_ABRT to prevent accessing unallocated memory.
|
||||
(ERR_ABRT now means the applicaiton has called tcp_abort!)
|
||||
|
||||
+++ Netconn API:
|
||||
* Changed netconn_receive() and netconn_accept() to return
|
||||
err_t, not a pointer to new data/netconn.
|
||||
|
||||
+++ Socket API:
|
||||
* LWIP_SO_RCVTIMEO: when accept() or recv() time out, they
|
||||
now set errno to EWOULDBLOCK/EAGAIN, not ETIMEDOUT.
|
||||
|
||||
* Added a minimal version of posix fctl() to have a
|
||||
standardised way to set O_NONBLOCK for nonblocking sockets.
|
||||
|
||||
+++ all APIs:
|
||||
* correctly implemented SO(F)_REUSEADDR
|
||||
|
||||
++ Port changes
|
||||
|
||||
+++ new files:
|
||||
|
||||
* Added 4 new files: def.c, timers.c, timers.h, tcp_impl.h:
|
||||
|
||||
* Moved stack-internal parts of tcp.h to tcp_impl.h, tcp.h now only contains
|
||||
the actual application programmer's API
|
||||
|
||||
* Separated timer implementation from sys.h/.c, moved to timers.h/.c;
|
||||
Added timer implementation for NO_SYS==1, set NO_SYS_NO_TIMERS==1 if you
|
||||
still want to use your own timer implementation for NO_SYS==0 (as before).
|
||||
|
||||
+++ sys layer:
|
||||
|
||||
* Converted mbox- and semaphore-functions to take pointers to sys_mbox_t/
|
||||
sys_sem_t;
|
||||
|
||||
* Converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
|
||||
|
||||
* Added Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX to let sys.h use
|
||||
binary semaphores instead of mutexes - as before)
|
||||
|
||||
+++ new options:
|
||||
|
||||
* Don't waste memory when chaining segments, added option TCP_OVERSIZE to
|
||||
prevent creating many small pbufs when calling tcp_write with many small
|
||||
blocks of data. Instead, pbufs are allocated larger than needed and the
|
||||
space is used for later calls to tcp_write.
|
||||
|
||||
* Added LWIP_NETIF_TX_SINGLE_PBUF to always copy to try to create single pbufs
|
||||
in tcp_write/udp_send.
|
||||
|
||||
* Added an additional option LWIP_ETHERNET to support ethernet without ARP
|
||||
(necessary for pure PPPoE)
|
||||
|
||||
* Add MEMP_SEPARATE_POOLS to place memory pools in separate arrays. This may
|
||||
be used to place these pools into user-defined memory by using external
|
||||
declaration.
|
||||
|
||||
* Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT
|
||||
|
||||
+++ new pools:
|
||||
|
||||
* Netdb uses a memp pool for allocating memory when getaddrinfo() is called,
|
||||
so MEMP_NUM_NETDB has to be set accordingly.
|
||||
|
||||
* DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses a memp pool instead of the heap, so
|
||||
MEMP_NUM_LOCALHOSTLIST has to be set accordingly.
|
||||
|
||||
* Snmp-agent uses a memp pools instead of the heap, so MEMP_NUM_SNMP_* have
|
||||
to be set accordingly.
|
||||
|
||||
* PPPoE uses a MEMP pool instead of the heap, so MEMP_NUM_PPPOE_INTERFACES
|
||||
has to be set accordingly
|
||||
|
||||
* Integrated loopif into netif.c - loopif does not have to be created by the
|
||||
port any more, just define LWIP_HAVE_LOOPIF to 1.
|
||||
|
||||
* Added define LWIP_RAND() for lwip-wide randomization (needs to be defined
|
||||
in cc.h, e.g. used by igmp)
|
||||
|
||||
* Added printf-formatter X8_F to printf u8_t as hex
|
||||
|
||||
* The heap now may be moved to user-defined memory by defining
|
||||
LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
|
||||
|
||||
* added autoip_set_struct() and dhcp_set_struct() to let autoip and dhcp work
|
||||
with user-allocated structs instead of calling mem_malloc
|
||||
|
||||
* Added const char* name to mem- and memp-stats for easier debugging.
|
||||
|
||||
* Calculate the TCP/UDP checksum while copying to only fetch data once:
|
||||
Define LWIP_CHKSUM_COPY to a memcpy-like function that returns the checksum
|
||||
|
||||
* Added SO_REUSE_RXTOALL to pass received UDP broadcast/multicast packets to
|
||||
more than one pcb.
|
||||
|
||||
* Changed the semantics of ARP_QUEUEING==0: ARP_QUEUEING now cannot be turned
|
||||
off any more, if this is set to 0, only one packet (the most recent one) is
|
||||
queued (like demanded by RFC 1122).
|
||||
|
||||
|
||||
++ Major bugfixes/improvements
|
||||
|
||||
* Implemented tcp_shutdown() to only shut down one end of a connection
|
||||
* Implemented shutdown() at socket- and netconn-level
|
||||
* Added errorset support to select() + improved select speed overhead
|
||||
* Merged pppd to v2.3.11 (including some backported bugfixes from 2.4.x)
|
||||
* Added timer implementation for NO_SYS==1 (may be disabled with NO_SYS_NO_TIMERS==1
|
||||
* Use macros defined in ip_addr.h to work with IP addresses
|
||||
* Implemented many nonblocking socket/netconn functions
|
||||
* Fixed ARP input processing: only add a new entry if a request was directed as us
|
||||
* mem_realloc() to mem_trim() to prevent confusion with realloc()
|
||||
* Some improvements for AutoIP (don't route/forward link-local addresses, don't break
|
||||
existing connections when assigning a routable address)
|
||||
* Correctly handle remote side overrunning our rcv_wnd in ooseq case
|
||||
* Removed packing from ip_addr_t, the packed version is now only used in protocol headers
|
||||
* Corrected PBUF_POOL_BUFSIZE for ports where ETH_PAD_SIZE > 0
|
||||
* Added support for static ARP table entries
|
||||
|
||||
(STABLE-1.3.2)
|
||||
|
||||
* initial version of this file
|
||||
9
doc/FILES
Normal file
9
doc/FILES
Normal file
@@ -0,0 +1,9 @@
|
||||
doxygen/ - Configuration files and scripts to create the lwIP doxygen source
|
||||
documentation (found at http://www.nongnu.org/lwip/)
|
||||
|
||||
savannah.txt - How to obtain the current development source code.
|
||||
contrib.txt - How to contribute to lwIP as a developer.
|
||||
rawapi.txt - The documentation for the core API of lwIP.
|
||||
Also provides an overview about the other APIs and multithreading.
|
||||
sys_arch.txt - The documentation for a system abstraction layer of lwIP.
|
||||
ppp.txt - Documentation of the PPP interface for lwIP.
|
||||
117
doc/NO_SYS_SampleCode.c
Normal file
117
doc/NO_SYS_SampleCode.c
Normal file
@@ -0,0 +1,117 @@
|
||||
void eth_mac_irq()
|
||||
{
|
||||
/* Service MAC IRQ here */
|
||||
|
||||
/* Allocate pbuf from pool (avoid using heap in interrupts) */
|
||||
struct pbuf* p = pbuf_alloc(PBUF_RAW, eth_data_count, PBUF_POOL);
|
||||
|
||||
if(p != NULL) {
|
||||
/* Copy ethernet frame into pbuf */
|
||||
pbuf_take(p, eth_data, eth_data_count);
|
||||
|
||||
/* Put in a queue which is processed in main loop */
|
||||
if(!queue_try_put(&queue, p)) {
|
||||
/* queue is full -> packet loss */
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static err_t netif_output(struct netif *netif, struct pbuf *p)
|
||||
{
|
||||
LINK_STATS_INC(link.xmit);
|
||||
|
||||
/* Update SNMP stats (only if you use SNMP) */
|
||||
MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len);
|
||||
int unicast = ((p->payload[0] & 0x01) == 0);
|
||||
if (unicast) {
|
||||
MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
|
||||
} else {
|
||||
MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
|
||||
}
|
||||
|
||||
lock_interrupts();
|
||||
pbuf_copy_partial(p, mac_send_buffer, p->tot_len, 0);
|
||||
/* Start MAC transmit here */
|
||||
unlock_interrupts();
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static void netif_status_callback(struct netif *netif)
|
||||
{
|
||||
printf("netif status changed %s\n", ip4addr_ntoa(netif_ip4_addr(netif)));
|
||||
}
|
||||
|
||||
static err_t netif_init(struct netif *netif)
|
||||
{
|
||||
netif->linkoutput = netif_output;
|
||||
netif->output = etharp_output;
|
||||
netif->output_ip6 = ethip6_output;
|
||||
netif->mtu = ETHERNET_MTU;
|
||||
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6;
|
||||
MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 100000000);
|
||||
|
||||
SMEMCPY(netif->hwaddr, your_mac_address_goes_here, sizeof(netif->hwaddr));
|
||||
netif->hwaddr_len = sizeof(netif->hwaddr);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
struct netif netif;
|
||||
|
||||
lwip_init();
|
||||
|
||||
netif_add(&netif, IP4_ADDR_ANY, IP4_ADDR_ANY, IP4_ADDR_ANY, NULL, netif_init, netif_input);
|
||||
netif.name[0] = 'e';
|
||||
netif.name[1] = '0';
|
||||
netif_create_ip6_linklocal_address(&netif, 1);
|
||||
netif.ip6_autoconfig_enabled = 1;
|
||||
netif_set_status_callback(&netif, netif_status_callback);
|
||||
netif_set_default(&netif);
|
||||
netif_set_up(&netif);
|
||||
|
||||
/* Start DHCP and HTTPD */
|
||||
dhcp_init();
|
||||
httpd_init();
|
||||
|
||||
while(1) {
|
||||
/* Check link state, e.g. via MDIO communication with PHY */
|
||||
if(link_state_changed()) {
|
||||
if(link_is_up()) {
|
||||
netif_set_link_up(&netif);
|
||||
} else {
|
||||
netif_set_link_down(&netif);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for received frames, feed them to lwIP */
|
||||
lock_interrupts();
|
||||
struct pbuf* p = queue_try_get(&queue);
|
||||
unlock_interrupts();
|
||||
|
||||
if(p != NULL) {
|
||||
LINK_STATS_INC(link.recv);
|
||||
|
||||
/* Update SNMP stats (only if you use SNMP) */
|
||||
MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
|
||||
int unicast = ((p->payload[0] & 0x01) == 0);
|
||||
if (unicast) {
|
||||
MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
|
||||
} else {
|
||||
MIB2_STATS_NETIF_INC(netif, ifinnucastpkts);
|
||||
}
|
||||
|
||||
if(netif.input(p, &netif) != ERR_OK) {
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/* Cyclic lwIP timers check */
|
||||
sys_check_timeouts();
|
||||
|
||||
/* your application goes here */
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ in lwIP development.
|
||||
2 How to contribute to lwIP
|
||||
|
||||
Here is a short list of suggestions to anybody working with lwIP and
|
||||
trying to contribute bugreports, fixes, enhancements, platform ports etc.
|
||||
trying to contribute bug reports, fixes, enhancements, platform ports etc.
|
||||
First of all as you may already know lwIP is a volunteer project so feedback
|
||||
to fixes or questions might often come late. Hopefully the bug and patch tracking
|
||||
features of Savannah help us not lose users' input.
|
||||
@@ -14,50 +14,45 @@ features of Savannah help us not lose users' input.
|
||||
2.1 Source code style:
|
||||
|
||||
1. do not use tabs.
|
||||
2. identation is two spaces per level.
|
||||
2. indentation is two spaces per level (i.e. per tab).
|
||||
3. end debug messages with a trailing newline (\n).
|
||||
4. one space between keyword and opening bracket.
|
||||
5. no space between function and opening bracket.
|
||||
6. one space and no newline before opening curly braces of a block.
|
||||
7. spaces surrounding assignment and comparisons.
|
||||
8. use current source code style as further reference.
|
||||
7. closing curly brace on a single line.
|
||||
8. spaces surrounding assignment and comparisons.
|
||||
9. don't initialize static and/or global variables to zero, the compiler takes care of that.
|
||||
10. use current source code style as further reference.
|
||||
|
||||
2.2 Source code documentation style:
|
||||
|
||||
1. JavaDoc compliant and Doxygen compatible.
|
||||
2. Function documentation above functions in .c files, not .h files.
|
||||
(This forces you to synchronize documentation and behaviour.)
|
||||
(This forces you to synchronize documentation and implementation.)
|
||||
3. Use current documentation style as further reference.
|
||||
|
||||
2.3 Bug reports and patches:
|
||||
|
||||
1. Make sure you are reporting bugs or send patches against the latest
|
||||
sources. (From the latest release and/or the current CVS sources.)
|
||||
sources. (From the latest release and/or the current Git sources.)
|
||||
2. If you think you found a bug make sure it's not already filed in the
|
||||
bugtracker at Savannah.
|
||||
3. If you have a fix put the patch on Savannah. If it is a patch that affects
|
||||
both core and arch specific stuff please separate them so that the core can
|
||||
be applied separately while leaving the other patch 'open'. The prefered way
|
||||
be applied separately while leaving the other patch 'open'. The preferred way
|
||||
is to NOT touch archs you can't test and let maintainers take care of them.
|
||||
This is a good way to see if they are used at all - the same goes for unix
|
||||
netifs except tapif.
|
||||
4. Do not file a bug and post a fix to it to the patch area. Either a bug report
|
||||
or a patch will be enough.
|
||||
If you correct an existing bug then attach the patch to the bug rather than creating a new entry in the patch area.
|
||||
5. Trivial patches (compiler warning, indentation and spelling fixes or anything obvious which takes a line or two)
|
||||
can go to the lwip-users list. This is still the fastest way of interaction and the list is not so crowded
|
||||
as to allow for loss of fixes. Putting bugs on Savannah and subsequently closing them is too much an overhead
|
||||
for reporting a compiler warning fix.
|
||||
6. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other
|
||||
trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you
|
||||
change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than
|
||||
5. Patches should be specific to a single change or to related changes. Do not mix bugfixes with spelling and other
|
||||
trivial fixes unless the bugfix is trivial too. Do not reorganize code and rename identifiers in the same patch you
|
||||
change behaviour if not necessary. A patch is easier to read and understand if it's to the point and short than
|
||||
if it's not to the point and long :) so the chances for it to be applied are greater.
|
||||
|
||||
2.4 Platform porters:
|
||||
|
||||
1. If you've ported lwIP to a platform (an OS, a uC/processor or a combination of these) and you think it
|
||||
could benefit others[1] you might want to post an url to a tarball or zip from which it can be imported
|
||||
to the contrib CVS module. Then you get CVS access and have to maintain your port :)
|
||||
|
||||
[1] - lwIP CVS should not be just a place to keep your port so you don't have to set up your own CVS :)
|
||||
Especially welcome are ports to common enough OS/hardware that others can have access too.
|
||||
1. If you have ported lwIP to a platform (an OS, a uC/processor or a combination of these) and
|
||||
you think it could benefit others[1] you might want discuss this on the mailing list. You
|
||||
can also ask for Git access to submit and maintain your port in the contrib Git module.
|
||||
|
||||
1
doc/doxygen/generate.bat
Normal file
1
doc/doxygen/generate.bat
Normal file
@@ -0,0 +1 @@
|
||||
doxygen lwip.Doxyfile
|
||||
1
doc/doxygen/generate.sh
Executable file
1
doc/doxygen/generate.sh
Executable file
@@ -0,0 +1 @@
|
||||
doxygen lwip.Doxyfile
|
||||
2505
doc/doxygen/lwip.Doxyfile
Normal file
2505
doc/doxygen/lwip.Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
126
doc/doxygen/main_page.h
Normal file
126
doc/doxygen/main_page.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* @defgroup lwip lwIP
|
||||
*
|
||||
* @defgroup infrastructure Infrastructure
|
||||
*
|
||||
* @defgroup callbackstyle_api Callback-style APIs
|
||||
* Non thread-safe APIs, callback style for maximum performance and minimum
|
||||
* memory footprint.
|
||||
*
|
||||
* @defgroup sequential_api Sequential-style APIs
|
||||
* Sequential-style APIs, blocking functions. More overhead, but can be called
|
||||
* from any thread except TCPIP thread.
|
||||
*
|
||||
* @defgroup addons Addons
|
||||
*
|
||||
* @defgroup apps Applications
|
||||
*/
|
||||
|
||||
/**
|
||||
* @mainpage Overview
|
||||
* @verbinclude "README"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page upgrading Upgrading
|
||||
* @verbinclude "UPGRADING"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page contrib How to contribute to lwIP
|
||||
* @verbinclude "contrib.txt"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page pitfalls Common pitfalls
|
||||
*
|
||||
* Multiple Execution Contexts in lwIP code
|
||||
* ========================================
|
||||
*
|
||||
* The most common source of lwIP problems is to have multiple execution contexts
|
||||
* inside the lwIP code.
|
||||
*
|
||||
* lwIP can be used in two basic modes: @ref lwip_nosys (no OS/RTOS
|
||||
* running on target system) or @ref lwip_os (there is an OS running
|
||||
* on the target system).
|
||||
*
|
||||
* Mainloop Mode
|
||||
* -------------
|
||||
* In mainloop mode, only @ref callbackstyle_api can be used.
|
||||
* The user has two possibilities to ensure there is only one
|
||||
* exection context at a time in lwIP:
|
||||
*
|
||||
* 1) Deliver RX ethernet packets directly in interrupt context to lwIP
|
||||
* by calling netif->input directly in interrupt. This implies all lwIP
|
||||
* callback functions are called in IRQ context, which may cause further
|
||||
* problems in application code: IRQ is blocked for a long time, multiple
|
||||
* execution contexts in application code etc. When the application wants
|
||||
* to call lwIP, it only needs to disable interrupts during the call.
|
||||
* If timers are involved, even more locking code is needed to lock out
|
||||
* timer IRQ and ethernet IRQ from each other, assuming these may be nested.
|
||||
*
|
||||
* 2) Run lwIP in a mainloop. There is example code here: @ref lwip_nosys.
|
||||
* lwIP is _ONLY_ called from mainloop callstacks here. The ethernet IRQ
|
||||
* has to put received telegrams into a queue which is polled in the
|
||||
* mainloop. Ensure lwIP is _NEVER_ called from an interrupt, e.g.
|
||||
* some SPI IRQ wants to forward data to udp_send() or tcp_write()!
|
||||
*
|
||||
* OS Mode
|
||||
* -------
|
||||
* In OS mode, @ref callbackstyle_api AND @ref sequential_api can be used.
|
||||
* @ref sequential_api are designed to be called from threads other than
|
||||
* the TCPIP thread, so there is nothing to consider here.
|
||||
* But @ref callbackstyle_api functions must _ONLY_ be called from
|
||||
* TCPIP thread. It is a common error to call these from other threads
|
||||
* or from IRQ contexts. Ethernet RX needs to deliver incoming packets
|
||||
* in the correct way by sending a message to TCPIP thread, this is
|
||||
* implemented in tcpip_input().
|
||||
* Again, ensure lwIP is _NEVER_ called from an interrupt, e.g.
|
||||
* some SPI IRQ wants to forward data to udp_send() or tcp_write()!
|
||||
*
|
||||
* 1) tcpip_callback() can be used get called back from TCPIP thread,
|
||||
* it is safe to call any @ref callbackstyle_api from there.
|
||||
*
|
||||
* 2) Use @ref LWIP_TCPIP_CORE_LOCKING. All @ref callbackstyle_api
|
||||
* functions can be called when lwIP core lock is aquired, see
|
||||
* @ref LOCK_TCPIP_CORE() and @ref UNLOCK_TCPIP_CORE().
|
||||
* These macros cannot be used in an interrupt context!
|
||||
* Note the OS must correctly handle priority inversion for this.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page bugs Reporting bugs
|
||||
* Please report bugs in the lwIP bug tracker at savannah.\n
|
||||
* BEFORE submitting, please check if the bug has already been reported!\n
|
||||
* https://savannah.nongnu.org/bugs/?group=lwip
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup lwip_nosys Mainloop mode ("NO_SYS")
|
||||
* @ingroup lwip
|
||||
* Use this mode if you do not run an OS on your system. \#define NO_SYS to 1.
|
||||
* Feed incoming packets to netif->input(pbuf, netif) function from mainloop,
|
||||
* *not* *from* *interrupt* *context*. You can allocate a @ref pbuf in interrupt
|
||||
* context and put them into a queue which is processed from mainloop.\n
|
||||
* Call sys_check_timeouts() periodically in the mainloop.\n
|
||||
* Porting: implement all functions in @ref sys_time and @ref sys_prot.\n
|
||||
* You can only use @ref callbackstyle_api in this mode.\n
|
||||
* Sample code:\n
|
||||
* @include NO_SYS_SampleCode.c
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup lwip_os OS mode (TCPIP thread)
|
||||
* @ingroup lwip
|
||||
* Use this mode if you run an OS on your system. It is recommended to
|
||||
* use an RTOS that correctly handles priority inversion and
|
||||
* to use @ref LWIP_TCPIP_CORE_LOCKING.\n
|
||||
* Porting: implement all functions in @ref sys_layer.\n
|
||||
* You can use @ref callbackstyle_api together with @ref tcpip_callback,
|
||||
* and all @ref sequential_api.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page raw_api lwIP API
|
||||
* @verbinclude "rawapi.txt"
|
||||
*/
|
||||
10
doc/doxygen/output/index.html
Normal file
10
doc/doxygen/output/index.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>Redirection</title>
|
||||
<meta http-equiv="refresh" content="0; url=html/index.html" />
|
||||
</head>
|
||||
<body>
|
||||
<a href="html/index.html">index.html</a>
|
||||
</body>
|
||||
</html>
|
||||
113
doc/mdns.txt
Normal file
113
doc/mdns.txt
Normal file
@@ -0,0 +1,113 @@
|
||||
Multicast DNS for lwIP
|
||||
|
||||
Author: Erik Ekman
|
||||
|
||||
|
||||
Note! The MDNS responder does not have all features required by the standards.
|
||||
See notes in src/apps/mdns/mdns.c for what is left. It is however usable in normal
|
||||
cases - but watch out if many devices on the same network try to use the same
|
||||
host/service instance names.
|
||||
|
||||
|
||||
How to enable:
|
||||
==============
|
||||
|
||||
MDNS support does not depend on DNS.
|
||||
MDNS supports using IPv4 only, v6 only, or v4+v6.
|
||||
|
||||
To enable MDNS responder, set
|
||||
LWIP_MDNS_RESPONDER = 1
|
||||
in lwipopts.h and add src/apps/mdns/mdns.c to your list of files to build.
|
||||
|
||||
The max number of services supported per netif is defined by MDNS_MAX_SERVICES,
|
||||
default is 1.
|
||||
|
||||
Increase MEMP_NUM_UDP_PCB by 1. MDNS needs one PCB.
|
||||
Increase LWIP_NUM_NETIF_CLIENT_DATA by 1 (MDNS needs one entry on netif).
|
||||
|
||||
MDNS with IPv4 requires LWIP_IGMP = 1, and preferably LWIP_AUTOIP = 1.
|
||||
MDNS with IPv6 requires LWIP_IPV6_MLD = 1, and that a link-local address is
|
||||
generated.
|
||||
|
||||
The MDNS code puts its structs on the stack where suitable to reduce dynamic
|
||||
memory allocation. It may use up to 1kB of stack.
|
||||
|
||||
MDNS needs a strncasecmp() implementation. If you have one, define
|
||||
LWIP_MDNS_STRNCASECMP to it. Otherwise the code will provide an implementation
|
||||
for you.
|
||||
|
||||
|
||||
How to use:
|
||||
===========
|
||||
|
||||
Call mdns_resp_init() during system initialization.
|
||||
This opens UDP sockets on port 5353 for IPv4 and IPv6.
|
||||
|
||||
|
||||
To start responding on a netif, run
|
||||
mdns_resp_add_netif(struct netif *netif, char *hostname, u32_t dns_ttl)
|
||||
|
||||
The hostname will be copied. If this returns successfully, the netif will join
|
||||
the multicast groups and any MDNS/legacy DNS requests sent unicast or multicast
|
||||
to port 5353 will be handled:
|
||||
- <hostname>.local type A, AAAA or ANY returns relevant IP addresses
|
||||
- Reverse lookups (PTR in-addr.arpa, ip6.arpa) of netif addresses
|
||||
returns <hostname>.local
|
||||
Answers will use the supplied TTL (in seconds)
|
||||
MDNS allows UTF-8 names, but it is recommended to stay within ASCII,
|
||||
since the default case-insensitive comparison assumes this.
|
||||
|
||||
It is recommended to call this function after an IPv4 address has been set,
|
||||
since there is currently no check if the v4 address is valid.
|
||||
|
||||
Call mdns_resp_netif_settings_changed() every time the IP address
|
||||
on the netif has changed.
|
||||
|
||||
To stop responding on a netif, run
|
||||
mdns_resp_remove_netif(struct netif *netif)
|
||||
|
||||
|
||||
Adding services:
|
||||
================
|
||||
|
||||
The netif first needs to be registered. Then run
|
||||
mdns_resp_add_service(struct netif *netif, char *name, char *service,
|
||||
u16_t proto, u16_t port, u32_t dns_ttl,
|
||||
service_get_txt_fn_t txt_fn, void *txt_userdata);
|
||||
|
||||
The name and service pointers will be copied. Name refers to the name of the
|
||||
service instance, and service is the type of service, like _http
|
||||
proto can be DNSSD_PROTO_UDP or DNSSD_PROTO_TCP which represent _udp and _tcp.
|
||||
If this call returns successfully, the following queries will be answered:
|
||||
- _services._dns-sd._udp.local type PTR returns <service>.<proto>.local
|
||||
- <service>.<proto>.local type PTR returns <name>.<service>.<proto>.local
|
||||
- <name>.<service>.<proto>.local type SRV returns hostname and port of service
|
||||
- <name>.<service>.<proto>.local type TXT builds text strings by calling txt_fn
|
||||
with the supplied userdata. The callback adds strings to the reply by calling
|
||||
mdns_resp_add_service_txtitem(struct mdns_service *service, char *txt,
|
||||
int txt_len). Example callback method:
|
||||
|
||||
static void srv_txt(struct mdns_service *service, void *txt_userdata)
|
||||
{
|
||||
res = mdns_resp_add_service_txtitem(service, "path=/", 6);
|
||||
LWIP_ERROR("mdns add service txt failed\n", (res == ERR_OK), return);
|
||||
}
|
||||
|
||||
Since a hostname struct is used for TXT storage each single item can be max
|
||||
63 bytes long, and the total max length (including length bytes for each
|
||||
item) is 255 bytes.
|
||||
|
||||
If your device runs a webserver on port 80, an example call might be:
|
||||
|
||||
mdns_resp_add_service(netif, "myweb", "_http"
|
||||
DNSSD_PROTO_TCP, 80, 3600, srv_txt, NULL);
|
||||
|
||||
which will publish myweb._http._tcp.local for any hosts looking for web servers,
|
||||
and point them to <hostname>.local:80
|
||||
|
||||
Relevant information will be sent as additional records to reduce number of
|
||||
requests required from a client.
|
||||
|
||||
Removing services is currently not supported. Services are removed when the
|
||||
netif is removed.
|
||||
|
||||
529
doc/ppp.txt
Normal file
529
doc/ppp.txt
Normal file
@@ -0,0 +1,529 @@
|
||||
PPP interface for lwIP
|
||||
|
||||
Author: Sylvain Rochet
|
||||
|
||||
Table of Contents:
|
||||
|
||||
1 - Supported PPP protocols and features
|
||||
2 - Raw API PPP example for all protocols
|
||||
3 - PPPoS input path (raw API, IRQ safe API, TCPIP API)
|
||||
4 - Thread safe PPP API (PPPAPI)
|
||||
5 - Notify phase callback (PPP_NOTIFY_PHASE)
|
||||
6 - Upgrading from lwIP <= 1.4.x to lwIP >= 2.0.x
|
||||
|
||||
|
||||
|
||||
1 Supported PPP protocols and features
|
||||
======================================
|
||||
|
||||
Supported Low level protocols:
|
||||
* PPP over serial using HDLC-like framing, such as wired dialup modems
|
||||
or mobile telecommunications GPRS/EDGE/UMTS/HSPA+/LTE modems
|
||||
* PPP over Ethernet, such as xDSL modems
|
||||
* PPP over L2TP (Layer 2 Tunneling Protocol) LAC (L2TP Access Concentrator),
|
||||
IP tunnel over UDP, such as VPN access
|
||||
|
||||
Supported auth protocols:
|
||||
* PAP, Password Authentication Protocol
|
||||
* CHAP, Challenge-Handshake Authentication Protocol, also known as CHAP-MD5
|
||||
* MSCHAPv1, Microsoft version of CHAP, version 1
|
||||
* MSCHAPv2, Microsoft version of CHAP, version 2
|
||||
* EAP, Extensible Authentication Protocol
|
||||
|
||||
Supported address protocols:
|
||||
* IPCP, IP Control Protocol, IPv4 addresses negotiation
|
||||
* IP6CP, IPv6 Control Protocol, IPv6 link-local addresses negotiation
|
||||
|
||||
Supported encryption protocols:
|
||||
* MPPE, Microsoft Point-to-Point Encryption
|
||||
|
||||
Supported compression or miscellaneous protocols, for serial links only:
|
||||
* PFC, Protocol Field Compression
|
||||
* ACFC, Address-and-Control-Field-Compression
|
||||
* ACCM, Asynchronous-Control-Character-Map
|
||||
* VJ, Van Jacobson TCP/IP Header Compression
|
||||
|
||||
|
||||
|
||||
2 Raw API PPP example for all protocols
|
||||
=======================================
|
||||
|
||||
As usual, raw API for lwIP means the lightweight API which *MUST* only be used
|
||||
for NO_SYS=1 systems or called inside lwIP core thread for NO_SYS=0 systems.
|
||||
|
||||
/*
|
||||
* Globals
|
||||
* =======
|
||||
*/
|
||||
|
||||
/* The PPP control block */
|
||||
ppp_pcb *ppp;
|
||||
|
||||
/* The PPP IP interface */
|
||||
struct netif ppp_netif;
|
||||
|
||||
|
||||
/*
|
||||
* PPP status callback
|
||||
* ===================
|
||||
*
|
||||
* PPP status callback is called on PPP status change (up, down, …) from lwIP
|
||||
* core thread
|
||||
*/
|
||||
|
||||
/* PPP status callback example */
|
||||
static void status_cb(ppp_pcb *pcb, int err_code, void *ctx) {
|
||||
struct netif *pppif = ppp_netif(pcb);
|
||||
LWIP_UNUSED_ARG(ctx);
|
||||
|
||||
switch(err_code) {
|
||||
case PPPERR_NONE: {
|
||||
#if LWIP_DNS
|
||||
const ip_addr_t *ns;
|
||||
#endif /* LWIP_DNS */
|
||||
printf("status_cb: Connected\n");
|
||||
#if PPP_IPV4_SUPPORT
|
||||
printf(" our_ipaddr = %s\n", ipaddr_ntoa(&pppif->ip_addr));
|
||||
printf(" his_ipaddr = %s\n", ipaddr_ntoa(&pppif->gw));
|
||||
printf(" netmask = %s\n", ipaddr_ntoa(&pppif->netmask));
|
||||
#if LWIP_DNS
|
||||
ns = dns_getserver(0);
|
||||
printf(" dns1 = %s\n", ipaddr_ntoa(ns));
|
||||
ns = dns_getserver(1);
|
||||
printf(" dns2 = %s\n", ipaddr_ntoa(ns));
|
||||
#endif /* LWIP_DNS */
|
||||
#endif /* PPP_IPV4_SUPPORT */
|
||||
#if PPP_IPV6_SUPPORT
|
||||
printf(" our6_ipaddr = %s\n", ip6addr_ntoa(netif_ip6_addr(pppif, 0)));
|
||||
#endif /* PPP_IPV6_SUPPORT */
|
||||
break;
|
||||
}
|
||||
case PPPERR_PARAM: {
|
||||
printf("status_cb: Invalid parameter\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_OPEN: {
|
||||
printf("status_cb: Unable to open PPP session\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_DEVICE: {
|
||||
printf("status_cb: Invalid I/O device for PPP\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_ALLOC: {
|
||||
printf("status_cb: Unable to allocate resources\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_USER: {
|
||||
printf("status_cb: User interrupt\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_CONNECT: {
|
||||
printf("status_cb: Connection lost\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_AUTHFAIL: {
|
||||
printf("status_cb: Failed authentication challenge\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_PROTOCOL: {
|
||||
printf("status_cb: Failed to meet protocol\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_PEERDEAD: {
|
||||
printf("status_cb: Connection timeout\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_IDLETIMEOUT: {
|
||||
printf("status_cb: Idle Timeout\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_CONNECTTIME: {
|
||||
printf("status_cb: Max connect time reached\n");
|
||||
break;
|
||||
}
|
||||
case PPPERR_LOOPBACK: {
|
||||
printf("status_cb: Loopback detected\n");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
printf("status_cb: Unknown error code %d\n", err_code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This should be in the switch case, this is put outside of the switch
|
||||
* case for example readability.
|
||||
*/
|
||||
|
||||
if (err_code == PPPERR_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* ppp_close() was previously called, don't reconnect */
|
||||
if (err_code == PPPERR_USER) {
|
||||
/* ppp_free(); -- can be called here */
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to reconnect in 30 seconds, if you need a modem chatscript you have
|
||||
* to do a much better signaling here ;-)
|
||||
*/
|
||||
ppp_connect(pcb, 30);
|
||||
/* OR ppp_listen(pcb); */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Creating a new PPPoS session
|
||||
* ============================
|
||||
*
|
||||
* In lwIP, PPPoS is not PPPoSONET, in lwIP PPPoS is PPPoSerial.
|
||||
*/
|
||||
|
||||
#include "netif/ppp/pppos.h"
|
||||
|
||||
/*
|
||||
* PPPoS serial output callback
|
||||
*
|
||||
* ppp_pcb, PPP control block
|
||||
* data, buffer to write to serial port
|
||||
* len, length of the data buffer
|
||||
* ctx, optional user-provided callback context pointer
|
||||
*
|
||||
* Return value: len if write succeed
|
||||
*/
|
||||
static u32_t output_cb(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) {
|
||||
return uart_write(UART, data, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new PPPoS interface
|
||||
*
|
||||
* ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
|
||||
* output_cb, PPPoS serial output callback
|
||||
* status_cb, PPP status callback, called on PPP status change (up, down, …)
|
||||
* ctx_cb, optional user-provided callback context pointer
|
||||
*/
|
||||
ppp = pppos_create(&ppp_netif,
|
||||
output_cb, status_cb, ctx_cb);
|
||||
|
||||
|
||||
/*
|
||||
* Creating a new PPPoE session
|
||||
* ============================
|
||||
*/
|
||||
|
||||
#include "netif/ppp/pppoe.h"
|
||||
|
||||
/*
|
||||
* Create a new PPPoE interface
|
||||
*
|
||||
* ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
|
||||
* ethif, already existing and setup Ethernet interface to use
|
||||
* service_name, PPPoE service name discriminator (not supported yet)
|
||||
* concentrator_name, PPPoE concentrator name discriminator (not supported yet)
|
||||
* status_cb, PPP status callback, called on PPP status change (up, down, …)
|
||||
* ctx_cb, optional user-provided callback context pointer
|
||||
*/
|
||||
ppp = pppoe_create(&ppp_netif,
|
||||
ðif,
|
||||
service_name, concentrator_name,
|
||||
status_cb, ctx_cb);
|
||||
|
||||
|
||||
/*
|
||||
* Creating a new PPPoL2TP session
|
||||
* ===============================
|
||||
*/
|
||||
|
||||
#include "netif/ppp/pppol2tp.h"
|
||||
|
||||
/*
|
||||
* Create a new PPPoL2TP interface
|
||||
*
|
||||
* ppp_netif, netif to use for this PPP link, i.e. PPP IP interface
|
||||
* netif, optional already existing and setup output netif, necessary if you
|
||||
* want to set this interface as default route to settle the chicken
|
||||
* and egg problem with VPN links
|
||||
* ipaddr, IP to connect to
|
||||
* port, UDP port to connect to (usually 1701)
|
||||
* secret, L2TP secret to use
|
||||
* secret_len, size in bytes of the L2TP secret
|
||||
* status_cb, PPP status callback, called on PPP status change (up, down, …)
|
||||
* ctx_cb, optional user-provided callback context pointer
|
||||
*/
|
||||
ppp = pppol2tp_create(&ppp_netif,
|
||||
struct netif *netif, ip_addr_t *ipaddr, u16_t port,
|
||||
u8_t *secret, u8_t secret_len,
|
||||
ppp_link_status_cb_fn link_status_cb, void *ctx_cb);
|
||||
|
||||
|
||||
/*
|
||||
* Initiate PPP client connection
|
||||
* ==============================
|
||||
*/
|
||||
|
||||
/* Set this interface as default route */
|
||||
ppp_set_default(ppp);
|
||||
|
||||
/*
|
||||
* Basic PPP client configuration. Can only be set if PPP session is in the
|
||||
* dead state (i.e. disconnected). We don't need to provide thread-safe
|
||||
* equivalents through PPPAPI because those helpers are only changing
|
||||
* structure members while session is inactive for lwIP core. Configuration
|
||||
* only need to be done once.
|
||||
*/
|
||||
|
||||
/* Ask the peer for up to 2 DNS server addresses. */
|
||||
ppp_set_usepeerdns(ppp, 1);
|
||||
|
||||
/* Auth configuration, this is pretty self-explanatory */
|
||||
ppp_set_auth(ppp, PPPAUTHTYPE_ANY, "login", "password");
|
||||
|
||||
/*
|
||||
* Initiate PPP negotiation, without waiting (holdoff=0), can only be called
|
||||
* if PPP session is in the dead state (i.e. disconnected).
|
||||
*/
|
||||
u16_t holdoff = 0;
|
||||
ppp_connect(ppp, holdoff);
|
||||
|
||||
|
||||
/*
|
||||
* Initiate PPP server listener
|
||||
* ============================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Basic PPP server configuration. Can only be set if PPP session is in the
|
||||
* dead state (i.e. disconnected). We don't need to provide thread-safe
|
||||
* equivalents through PPPAPI because those helpers are only changing
|
||||
* structure members while session is inactive for lwIP core. Configuration
|
||||
* only need to be done once.
|
||||
*/
|
||||
ip4_addr_t addr;
|
||||
|
||||
/* Set our address */
|
||||
IP4_ADDR(&addr, 192,168,0,1);
|
||||
ppp_set_ipcp_ouraddr(ppp, &addr);
|
||||
|
||||
/* Set peer(his) address */
|
||||
IP4_ADDR(&addr, 192,168,0,2);
|
||||
ppp_set_ipcp_hisaddr(ppp, &addr);
|
||||
|
||||
/* Set primary DNS server */
|
||||
IP4_ADDR(&addr, 192,168,10,20);
|
||||
ppp_set_ipcp_dnsaddr(ppp, 0, &addr);
|
||||
|
||||
/* Set secondary DNS server */
|
||||
IP4_ADDR(&addr, 192,168,10,21);
|
||||
ppp_set_ipcp_dnsaddr(ppp, 1, &addr);
|
||||
|
||||
/* Auth configuration, this is pretty self-explanatory */
|
||||
ppp_set_auth(ppp, PPPAUTHTYPE_ANY, "login", "password");
|
||||
|
||||
/* Require peer to authenticate */
|
||||
ppp_set_auth_required(ppp, 1);
|
||||
|
||||
/*
|
||||
* Only for PPPoS, the PPP session should be up and waiting for input.
|
||||
*
|
||||
* Note: for PPPoS, ppp_connect() and ppp_listen() are actually the same thing.
|
||||
* The listen call is meant for future support of PPPoE and PPPoL2TP server
|
||||
* mode, where we will need to negotiate the incoming PPPoE session or L2TP
|
||||
* session before initiating PPP itself. We need this call because there is
|
||||
* two passive modes for PPPoS, ppp_set_passive and ppp_set_silent.
|
||||
*/
|
||||
ppp_set_silent(pppos, 1);
|
||||
|
||||
/*
|
||||
* Initiate PPP listener (i.e. wait for an incoming connection), can only
|
||||
* be called if PPP session is in the dead state (i.e. disconnected).
|
||||
*/
|
||||
ppp_listen(ppp);
|
||||
|
||||
|
||||
/*
|
||||
* Closing PPP connection
|
||||
* ======================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Initiate the end of the PPP session, without carrier lost signal
|
||||
* (nocarrier=0), meaning a clean shutdown of PPP protocols.
|
||||
* You can call this function at anytime.
|
||||
*/
|
||||
u8_t nocarrier = 0;
|
||||
ppp_close(ppp, nocarrier);
|
||||
/*
|
||||
* Then you must wait your status_cb() to be called, it may takes from a few
|
||||
* seconds to several tens of seconds depending on the current PPP state.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Freeing a PPP connection
|
||||
* ========================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Free the PPP control block, can only be called if PPP session is in the
|
||||
* dead state (i.e. disconnected). You need to call ppp_close() before.
|
||||
*/
|
||||
ppp_free(ppp);
|
||||
|
||||
|
||||
|
||||
3 PPPoS input path (raw API, IRQ safe API, TCPIP API)
|
||||
=====================================================
|
||||
|
||||
Received data on serial port should be sent to lwIP using the pppos_input()
|
||||
function or the pppos_input_tcpip() function.
|
||||
|
||||
If NO_SYS is 1 and if PPP_INPROC_IRQ_SAFE is 0 (the default), pppos_input()
|
||||
is not IRQ safe and then *MUST* only be called inside your main loop.
|
||||
|
||||
Whatever the NO_SYS value, if PPP_INPROC_IRQ_SAFE is 1, pppos_input() is IRQ
|
||||
safe and can be safely called from an interrupt context, using that is going
|
||||
to reduce your need of buffer if pppos_input() is called byte after byte in
|
||||
your rx serial interrupt.
|
||||
|
||||
if NO_SYS is 0, the thread safe way outside an interrupt context is to use
|
||||
the pppos_input_tcpip() function to pass input data to the lwIP core thread
|
||||
using the TCPIP API. This is thread safe in all cases but you should avoid
|
||||
passing data byte after byte because it uses heavy locking (mailbox) and it
|
||||
allocates pbuf, better fill them !
|
||||
|
||||
if NO_SYS is 0 and if PPP_INPROC_IRQ_SAFE is 1, you may also use pppos_input()
|
||||
from an RX thread, however pppos_input() is not thread safe by itself. You can
|
||||
do that *BUT* you should NEVER call pppos_connect(), pppos_listen() and
|
||||
ppp_free() if pppos_input() can still be running, doing this is NOT thread safe
|
||||
at all. Using PPP_INPROC_IRQ_SAFE from an RX thread is discouraged unless you
|
||||
really know what you are doing, your move ;-)
|
||||
|
||||
|
||||
/*
|
||||
* Fonction to call for received data
|
||||
*
|
||||
* ppp, PPP control block
|
||||
* buffer, input buffer
|
||||
* buffer_len, buffer length in bytes
|
||||
*/
|
||||
void pppos_input(ppp, buffer, buffer_len);
|
||||
|
||||
or
|
||||
|
||||
void pppos_input_tcpip(ppp, buffer, buffer_len);
|
||||
|
||||
|
||||
|
||||
4 Thread safe PPP API (PPPAPI)
|
||||
==============================
|
||||
|
||||
There is a thread safe API for all corresponding ppp_* functions, you have to
|
||||
enable LWIP_PPP_API in your lwipopts.h file, then see
|
||||
include/netif/ppp/pppapi.h, this is actually pretty obvious.
|
||||
|
||||
|
||||
|
||||
5 Notify phase callback (PPP_NOTIFY_PHASE)
|
||||
==========================================
|
||||
|
||||
Notify phase callback, enabled using the PPP_NOTIFY_PHASE config option, let
|
||||
you configure a callback that is called on each PPP internal state change.
|
||||
This is different from the status callback which only warns you about
|
||||
up(running) and down(dead) events.
|
||||
|
||||
Notify phase callback can be used, for example, to set a LED pattern depending
|
||||
on the current phase of the PPP session. Here is a callback example which
|
||||
tries to mimic what we usually see on xDSL modems while they are negotiating
|
||||
the link, which should be self-explanatory:
|
||||
|
||||
static void ppp_notify_phase_cb(ppp_pcb *pcb, u8_t phase, void *ctx) {
|
||||
switch (phase) {
|
||||
|
||||
/* Session is down (either permanently or briefly) */
|
||||
case PPP_PHASE_DEAD:
|
||||
led_set(PPP_LED, LED_OFF);
|
||||
break;
|
||||
|
||||
/* We are between two sessions */
|
||||
case PPP_PHASE_HOLDOFF:
|
||||
led_set(PPP_LED, LED_SLOW_BLINK);
|
||||
break;
|
||||
|
||||
/* Session just started */
|
||||
case PPP_PHASE_INITIALIZE:
|
||||
led_set(PPP_LED, LED_FAST_BLINK);
|
||||
break;
|
||||
|
||||
/* Session is running */
|
||||
case PPP_PHASE_RUNNING:
|
||||
led_set(PPP_LED, LED_ON);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
6 Upgrading from lwIP <= 1.4.x to lwIP >= 2.0.x
|
||||
===============================================
|
||||
|
||||
PPP API was fully reworked between 1.4.x and 2.0.x releases. However porting
|
||||
from previous lwIP version is pretty easy:
|
||||
|
||||
* Previous PPP API used an integer to identify PPP sessions, we are now
|
||||
using ppp_pcb* control block, therefore all functions changed from "int ppp"
|
||||
to "ppp_pcb *ppp"
|
||||
|
||||
* struct netif was moved outside the PPP structure, you have to provide a netif
|
||||
for PPP interface in pppoX_create() functions
|
||||
|
||||
* PPP session are not started automatically after you created them anymore,
|
||||
you have to call ppp_connect(), this way you can configure the session before
|
||||
starting it.
|
||||
|
||||
* Previous PPP API used CamelCase, we are now using snake_case.
|
||||
|
||||
* Previous PPP API mixed PPPoS and PPPoE calls, this isn't the case anymore,
|
||||
PPPoS functions are now prefixed pppos_ and PPPoE functions are now prefixed
|
||||
pppoe_, common functions are now prefixed ppp_.
|
||||
|
||||
* New PPPERR_ error codes added, check you have all of them in your status
|
||||
callback function
|
||||
|
||||
* Only the following include files should now be used in user application:
|
||||
#include "netif/ppp/pppapi.h"
|
||||
#include "netif/ppp/pppos.h"
|
||||
#include "netif/ppp/pppoe.h"
|
||||
#include "netif/ppp/pppol2tp.h"
|
||||
|
||||
Functions from ppp.h can be used, but you don't need to include this header
|
||||
file as it is already included by above header files.
|
||||
|
||||
* PPP_INPROC_OWNTHREAD was broken by design and was removed, you have to create
|
||||
your own serial rx thread
|
||||
|
||||
* PPP_INPROC_MULTITHREADED option was misnamed and confusing and was renamed
|
||||
PPP_INPROC_IRQ_SAFE, please read the "PPPoS input path" documentation above
|
||||
because you might have been fooled by that
|
||||
|
||||
* If you used tcpip_callback_with_block() on ppp_ functions you may wish to use
|
||||
the PPPAPI API instead.
|
||||
|
||||
* ppp_sighup and ppp_close functions were merged using an optional argument
|
||||
"nocarrier" on ppp_close.
|
||||
|
||||
* DNS servers are now only remotely asked if LWIP_DNS is set and if
|
||||
ppp_set_usepeerdns() is set to true, they are now automatically registered
|
||||
using the dns_setserver() function so you don't need to do that in the PPP
|
||||
callback anymore.
|
||||
|
||||
* PPPoS does not use the SIO API anymore, as such it now requires a serial
|
||||
output callback in place of sio_write
|
||||
|
||||
* PPP_MAXIDLEFLAG is now in ms instead of jiffies
|
||||
300
doc/rawapi.txt
300
doc/rawapi.txt
@@ -1,20 +1,76 @@
|
||||
Raw TCP/IP interface for lwIP 0.5
|
||||
Raw TCP/IP interface for lwIP
|
||||
|
||||
Author: Adam Dunkels
|
||||
Authors: Adam Dunkels, Leon Woestenberg, Christiaan Simons
|
||||
|
||||
lwIP provides two Application Program's Interfaces (APIs) for programs
|
||||
to use for communication with the TCP/IP code: the sequential API
|
||||
(often just called "the API") and the raw TCP/IP interface. This
|
||||
document is intended as a description of the latter. For lwIP versions
|
||||
lower than 0.5, this API was not documented.
|
||||
lwIP provides three Application Program's Interfaces (APIs) for programs
|
||||
to use for communication with the TCP/IP code:
|
||||
* low-level "core" / "callback" or "raw" API.
|
||||
* higher-level "sequential" API.
|
||||
* BSD-style socket API.
|
||||
|
||||
The raw API (sometimes called native API) is an event-driven API designed
|
||||
to be used without an operating system that implements zero-copy send and
|
||||
receive. This API is also used by the core stack for interaction between
|
||||
the various protocols. It is the only API available when running lwIP
|
||||
without an operating system.
|
||||
|
||||
The sequential API provides a way for ordinary, sequential, programs
|
||||
to use the lwIP stack. It is quite similar to the BSD socket API. The
|
||||
model of execution is based on the open-read-write-close
|
||||
model of execution is based on the blocking open-read-write-close
|
||||
paradigm. Since the TCP/IP stack is event based by nature, the TCP/IP
|
||||
code and the application program must reside in different execution
|
||||
contexts (threads).
|
||||
|
||||
The socket API is a compatibility API for existing applications,
|
||||
currently it is built on top of the sequential API. It is meant to
|
||||
provide all functions needed to run socket API applications running
|
||||
on other platforms (e.g. unix / windows etc.). However, due to limitations
|
||||
in the specification of this API, there might be incompatibilities
|
||||
that require small modifications of existing programs.
|
||||
|
||||
** Multithreading
|
||||
|
||||
lwIP started targeting single-threaded environments. When adding multi-
|
||||
threading support, instead of making the core thread-safe, another
|
||||
approach was chosen: there is one main thread running the lwIP core
|
||||
(also known as the "tcpip_thread"). When running in a multithreaded
|
||||
environment, raw API functions MUST only be called from the core thread
|
||||
since raw API functions are not protected from concurrent access (aside
|
||||
from pbuf- and memory management functions). Application threads using
|
||||
the sequential- or socket API communicate with this main thread through
|
||||
message passing.
|
||||
|
||||
As such, the list of functions that may be called from
|
||||
other threads or an ISR is very limited! Only functions
|
||||
from these API header files are thread-safe:
|
||||
- api.h
|
||||
- netbuf.h
|
||||
- netdb.h
|
||||
- netifapi.h
|
||||
- pppapi.h
|
||||
- sockets.h
|
||||
- sys.h
|
||||
|
||||
Additionaly, memory (de-)allocation functions may be
|
||||
called from multiple threads (not ISR!) with NO_SYS=0
|
||||
since they are protected by SYS_LIGHTWEIGHT_PROT and/or
|
||||
semaphores.
|
||||
|
||||
Netconn or Socket API functions are thread safe against the
|
||||
core thread but they are not reentrant at the control block
|
||||
granularity level. That is, a UDP or TCP control block must
|
||||
not be shared among multiple threads without proper locking.
|
||||
|
||||
If SYS_LIGHTWEIGHT_PROT is set to 1 and
|
||||
LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1,
|
||||
pbuf_free() may also be called from another thread or
|
||||
an ISR (since only then, mem_free - for PBUF_RAM - may
|
||||
be called from an ISR: otherwise, the HEAP is only
|
||||
protected by semaphores).
|
||||
|
||||
|
||||
** The remainder of this document discusses the "raw" API. **
|
||||
|
||||
The raw TCP/IP interface allows the application program to integrate
|
||||
better with the TCP/IP code. Program execution is event based by
|
||||
having callback functions being called from within the TCP/IP
|
||||
@@ -30,14 +86,28 @@ the raw TCP/IP interface are more difficult to understand. Still, this
|
||||
is the preferred way of writing applications that should be small in
|
||||
code size and memory usage.
|
||||
|
||||
Both APIs can be used simultaneously by different application
|
||||
All APIs can be used simultaneously by different application
|
||||
programs. In fact, the sequential API is implemented as an application
|
||||
program using the raw TCP/IP interface.
|
||||
|
||||
Do not confuse the lwIP raw API with raw Ethernet or IP sockets.
|
||||
The former is a way of interfacing the lwIP network stack (including
|
||||
TCP and UDP), the later refers to processing raw Ethernet or IP data
|
||||
instead of TCP connections or UDP packets.
|
||||
|
||||
Raw API applications may never block since all packet processing
|
||||
(input and output) as well as timer processing (TCP mainly) is done
|
||||
in a single execution context.
|
||||
|
||||
--- Callbacks
|
||||
|
||||
Program execution is driven by callbacks. Each callback is an ordinary
|
||||
Program execution is driven by callbacks functions, which are then
|
||||
invoked by the lwIP core when activity related to that application
|
||||
occurs. A particular application may register to be notified via a
|
||||
callback function for events such as incoming data available, outgoing
|
||||
data sent, error notifications, poll timer expiration, connection
|
||||
closed, etc. An application can provide a callback function to perform
|
||||
processing for any or all of these events. Each callback is an ordinary
|
||||
C function that is called from within the TCP/IP code. Every callback
|
||||
function is passed the current TCP or UDP connection state as an
|
||||
argument. Also, in order to be able to keep program specific state,
|
||||
@@ -67,7 +137,7 @@ incoming connections or be explicitly connected to another host.
|
||||
Creates a new connection identifier (PCB). If memory is not
|
||||
available for creating the new pcb, NULL is returned.
|
||||
|
||||
- err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr,
|
||||
- err_t tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr,
|
||||
u16_t port)
|
||||
|
||||
Binds the pcb to a local IP address and port number. The IP address
|
||||
@@ -95,14 +165,20 @@ incoming connections or be explicitly connected to another host.
|
||||
listening connection. If so, the memory associated with the pcb
|
||||
passed as an argument to tcp_listen() will not be deallocated.
|
||||
|
||||
- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
|
||||
|
||||
Same as tcp_listen, but limits the number of outstanding connections
|
||||
in the listen queue to the value specified by the backlog argument.
|
||||
To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h.
|
||||
|
||||
- void tcp_accept(struct tcp_pcb *pcb,
|
||||
err_t (* accept)(void *arg, struct tcp_pcb *newpcb,
|
||||
err_t err))
|
||||
|
||||
Specified the callback function that should be called when a new
|
||||
connection arrives on a listening connection.
|
||||
|
||||
- err_t tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr,
|
||||
|
||||
- err_t tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr,
|
||||
u16_t port, err_t (* connected)(void *arg,
|
||||
struct tcp_pcb *tpcb,
|
||||
err_t err));
|
||||
@@ -115,14 +191,15 @@ incoming connections or be explicitly connected to another host.
|
||||
function specified as the fourth argument (the "connected" argument)
|
||||
when the connection is established. If the connection could not be
|
||||
properly established, either because the other host refused the
|
||||
connection or because the other host didn't answer, the "connected"
|
||||
function will be called with an the "err" argument set accordingly.
|
||||
connection or because the other host didn't answer, the "err"
|
||||
callback function of this pcb (registered with tcp_err, see below)
|
||||
will be called.
|
||||
|
||||
The tcp_connect() function can return ERR_MEM if no memory is
|
||||
available for enqueueing the SYN segment. If the SYN indeed was
|
||||
enqueued successfully, the tcp_connect() function returns ERR_OK.
|
||||
|
||||
|
||||
|
||||
--- Sending TCP data
|
||||
|
||||
TCP data is sent by enqueueing the data with a call to
|
||||
@@ -130,15 +207,19 @@ tcp_write(). When the data is successfully transmitted to the remote
|
||||
host, the application will be notified with a call to a specified
|
||||
callback function.
|
||||
|
||||
- err_t tcp_write(struct tcp_pcb *pcb, void *dataptr, u16_t len,
|
||||
u8_t copy)
|
||||
- err_t tcp_write(struct tcp_pcb *pcb, const void *dataptr, u16_t len,
|
||||
u8_t apiflags)
|
||||
|
||||
Enqueues the data pointed to by the argument dataptr. The length of
|
||||
the data is passed as the len parameter. The copy argument is either
|
||||
0 or 1 and indicates whether the new memory should be allocated for
|
||||
the data to be copied into. If the argument is 0, no new memory
|
||||
should be allocated and the data should only be referenced by
|
||||
pointer.
|
||||
the data is passed as the len parameter. The apiflags can be one or more of:
|
||||
- TCP_WRITE_FLAG_COPY: indicates whether the new memory should be allocated
|
||||
for the data to be copied into. If this flag is not given, no new memory
|
||||
should be allocated and the data should only be referenced by pointer. This
|
||||
also means that the memory behind dataptr must not change until the data is
|
||||
ACKed by the remote host
|
||||
- TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is omitted,
|
||||
the PSH flag is set in the last segment created by this call to tcp_write.
|
||||
If this flag is given, the PSH flag is not set.
|
||||
|
||||
The tcp_write() function will fail and return ERR_MEM if the length
|
||||
of the data exceeds the current send buffer size or if the length of
|
||||
@@ -153,7 +234,7 @@ callback function.
|
||||
|
||||
- void tcp_sent(struct tcp_pcb *pcb,
|
||||
err_t (* sent)(void *arg, struct tcp_pcb *tpcb,
|
||||
u16_t len))
|
||||
u16_t len))
|
||||
|
||||
Specifies the callback function that should be called when data has
|
||||
successfully been received (i.e., acknowledged) by the remote
|
||||
@@ -175,13 +256,16 @@ window.
|
||||
|
||||
Sets the callback function that will be called when new data
|
||||
arrives. The callback function will be passed a NULL pbuf to
|
||||
indicate that the remote host has closed the connection.
|
||||
indicate that the remote host has closed the connection. If
|
||||
there are no errors and the callback function is to return
|
||||
ERR_OK, then it must free the pbuf. Otherwise, it must not
|
||||
free the pbuf so that lwIP core code can store it.
|
||||
|
||||
- void tcp_recved(struct tcp_pcb *pcb, u16_t len)
|
||||
|
||||
Must be called when the application has received the data. The len
|
||||
argument indicates the length of the received data.
|
||||
|
||||
|
||||
|
||||
--- Application polling
|
||||
|
||||
@@ -194,8 +278,9 @@ if a call to tcp_write() has failed because memory wasn't available,
|
||||
the application may use the polling functionality to call tcp_write()
|
||||
again when the connection has been idle for a while.
|
||||
|
||||
- void tcp_poll(struct tcp_pcb *pcb, u8_t interval,
|
||||
err_t (* poll)(void *arg, struct tcp_pcb *tpcb))
|
||||
- void tcp_poll(struct tcp_pcb *pcb,
|
||||
err_t (* poll)(void *arg, struct tcp_pcb *tpcb),
|
||||
u8_t interval)
|
||||
|
||||
Specifies the polling interval and the callback function that should
|
||||
be called to poll the application. The interval is specified in
|
||||
@@ -221,29 +306,23 @@ again when the connection has been idle for a while.
|
||||
Aborts the connection by sending a RST (reset) segment to the remote
|
||||
host. The pcb is deallocated. This function never fails.
|
||||
|
||||
ATTENTION: When calling this from one of the TCP callbacks, make
|
||||
sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
|
||||
or you will risk accessing deallocated memory or memory leaks!
|
||||
|
||||
|
||||
If a connection is aborted because of an error, the application is
|
||||
alerted of this event by the err callback. Errors that might abort a
|
||||
connection are when there is a shortage of memory. The callback
|
||||
function to be called is set using the tcp_err() function.
|
||||
|
||||
- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg,
|
||||
err_t err))
|
||||
err_t err))
|
||||
|
||||
The error callback function does not get the pcb passed to it as a
|
||||
parameter since the pcb may already have been deallocated.
|
||||
|
||||
|
||||
--- Lower layer TCP interface
|
||||
|
||||
TCP provides a simple interface to the lower layers of the
|
||||
system. During system initialization, the function tcp_init() has
|
||||
to be called before any other TCP function is called. When the system
|
||||
is running, the two timer functions tcp_fasttmr() and tcp_slowtmr()
|
||||
must be called with regular intervals. The tcp_fasttmr() should be
|
||||
called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and
|
||||
tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds.
|
||||
|
||||
|
||||
--- UDP interface
|
||||
|
||||
The UDP interface is similar to that of TCP, but due to the lower
|
||||
@@ -259,19 +338,24 @@ level of complexity of UDP, the interface is significantly simpler.
|
||||
|
||||
Removes and deallocates the pcb.
|
||||
|
||||
- err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr,
|
||||
- err_t udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr,
|
||||
u16_t port)
|
||||
|
||||
Binds the pcb to a local address. The IP-address argument "ipaddr"
|
||||
can be IP_ADDR_ANY to indicate that it should listen to any local IP
|
||||
address. The function currently always return ERR_OK.
|
||||
|
||||
- err_t udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr,
|
||||
- err_t udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr,
|
||||
u16_t port)
|
||||
|
||||
Sets the remote end of the pcb. This function does not generate any
|
||||
network traffic, but only set the remote address of the pcb.
|
||||
|
||||
- err_t udp_disconnect(struct udp_pcb *pcb)
|
||||
|
||||
Remove the remote end of the pcb. This function does not generate
|
||||
any network traffic, but only removes the remote address of the pcb.
|
||||
|
||||
- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)
|
||||
|
||||
Sends the pbuf p. The pbuf is not deallocated.
|
||||
@@ -279,9 +363,137 @@ level of complexity of UDP, the interface is significantly simpler.
|
||||
- void udp_recv(struct udp_pcb *pcb,
|
||||
void (* recv)(void *arg, struct udp_pcb *upcb,
|
||||
struct pbuf *p,
|
||||
struct ip_addr *addr,
|
||||
ip_addr_t *addr,
|
||||
u16_t port),
|
||||
void *recv_arg)
|
||||
|
||||
Specifies a callback function that should be called when a UDP
|
||||
datagram is received.
|
||||
datagram is received.
|
||||
|
||||
|
||||
--- System initalization
|
||||
|
||||
A truly complete and generic sequence for initializing the lwIP stack
|
||||
cannot be given because it depends on additional initializations for
|
||||
your runtime environment (e.g. timers).
|
||||
|
||||
We can give you some idea on how to proceed when using the raw API.
|
||||
We assume a configuration using a single Ethernet netif and the
|
||||
UDP and TCP transport layers, IPv4 and the DHCP client.
|
||||
|
||||
Call these functions in the order of appearance:
|
||||
|
||||
- lwip_init()
|
||||
|
||||
Initialize the lwIP stack and all of its subsystems.
|
||||
|
||||
- netif_add(struct netif *netif, const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask, const ip4_addr_t *gw,
|
||||
void *state, netif_init_fn init, netif_input_fn input)
|
||||
|
||||
Adds your network interface to the netif_list. Allocate a struct
|
||||
netif and pass a pointer to this structure as the first argument.
|
||||
Give pointers to cleared ip_addr structures when using DHCP,
|
||||
or fill them with sane numbers otherwise. The state pointer may be NULL.
|
||||
|
||||
The init function pointer must point to a initialization function for
|
||||
your Ethernet netif interface. The following code illustrates its use.
|
||||
|
||||
err_t netif_if_init(struct netif *netif)
|
||||
{
|
||||
u8_t i;
|
||||
|
||||
for (i = 0; i < ETHARP_HWADDR_LEN; i++) {
|
||||
netif->hwaddr[i] = some_eth_addr[i];
|
||||
}
|
||||
init_my_eth_device();
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
For Ethernet drivers, the input function pointer must point to the lwIP
|
||||
function ethernet_input() declared in "netif/etharp.h". Other drivers
|
||||
must use ip_input() declared in "lwip/ip.h".
|
||||
|
||||
- netif_set_default(struct netif *netif)
|
||||
|
||||
Registers the default network interface.
|
||||
|
||||
- netif_set_link_up(struct netif *netif)
|
||||
|
||||
This is the hardware link state; e.g. whether cable is plugged for wired
|
||||
Ethernet interface. This function must be called even if you don't know
|
||||
the current state. Having link up and link down events is optional but
|
||||
DHCP and IPv6 discover benefit well from those events.
|
||||
|
||||
- netif_set_up(struct netif *netif)
|
||||
|
||||
This is the administrative (= software) state of the netif, when the
|
||||
netif is fully configured this function must be called.
|
||||
|
||||
- dhcp_start(struct netif *netif)
|
||||
|
||||
Creates a new DHCP client for this interface on the first call.
|
||||
|
||||
You can peek in the netif->dhcp struct for the actual DHCP status.
|
||||
|
||||
- sys_check_timeouts()
|
||||
|
||||
When the system is running, you have to periodically call
|
||||
sys_check_timeouts() which will handle all timers for all protocols in
|
||||
the stack; add this to your main loop or equivalent.
|
||||
|
||||
|
||||
--- Optimalization hints
|
||||
|
||||
The first thing you want to optimize is the lwip_standard_checksum()
|
||||
routine from src/core/inet.c. You can override this standard
|
||||
function with the #define LWIP_CHKSUM <your_checksum_routine>.
|
||||
|
||||
There are C examples given in inet.c or you might want to
|
||||
craft an assembly function for this. RFC1071 is a good
|
||||
introduction to this subject.
|
||||
|
||||
Other significant improvements can be made by supplying
|
||||
assembly or inline replacements for htons() and htonl()
|
||||
if you're using a little-endian architecture.
|
||||
#define lwip_htons(x) <your_htons>
|
||||
#define lwip_htonl(x) <your_htonl>
|
||||
If you #define them to htons() and htonl(), you should
|
||||
#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS to prevent lwIP from
|
||||
defining hton*/ntoh* compatibility macros.
|
||||
|
||||
Check your network interface driver if it reads at
|
||||
a higher speed than the maximum wire-speed. If the
|
||||
hardware isn't serviced frequently and fast enough
|
||||
buffer overflows are likely to occur.
|
||||
|
||||
E.g. when using the cs8900 driver, call cs8900if_service(ethif)
|
||||
as frequently as possible. When using an RTOS let the cs8900 interrupt
|
||||
wake a high priority task that services your driver using a binary
|
||||
semaphore or event flag. Some drivers might allow additional tuning
|
||||
to match your application and network.
|
||||
|
||||
For a production release it is recommended to set LWIP_STATS to 0.
|
||||
Note that speed performance isn't influenced much by simply setting
|
||||
high values to the memory options.
|
||||
|
||||
For more optimization hints take a look at the lwIP wiki.
|
||||
|
||||
--- Zero-copy MACs
|
||||
|
||||
To achieve zero-copy on transmit, the data passed to the raw API must
|
||||
remain unchanged until sent. Because the send- (or write-)functions return
|
||||
when the packets have been enqueued for sending, data must be kept stable
|
||||
after that, too.
|
||||
|
||||
This implies that PBUF_RAM/PBUF_POOL pbufs passed to raw-API send functions
|
||||
must *not* be reused by the application unless their ref-count is 1.
|
||||
|
||||
For no-copy pbufs (PBUF_ROM/PBUF_REF), data must be kept unchanged, too,
|
||||
but the stack/driver will/must copy PBUF_REF'ed data when enqueueing, while
|
||||
PBUF_ROM-pbufs are just enqueued (as ROM-data is expected to never change).
|
||||
|
||||
Also, data passed to tcp_write without the copy-flag must not be changed!
|
||||
|
||||
Therefore, be careful which type of PBUF you use and if you copy TCP data
|
||||
or not!
|
||||
|
||||
165
doc/savannah.txt
165
doc/savannah.txt
@@ -2,110 +2,119 @@ Daily Use Guide for using Savannah for lwIP
|
||||
|
||||
Table of Contents:
|
||||
|
||||
1 - Anonymous CVS checkouts and updates (to be elaborated)
|
||||
2 - Committers/developers CVS access using SSH (to be written)
|
||||
3 - Merging from DEVEL branch to main trunk (stable branch)
|
||||
1 - Obtaining lwIP from the Git repository
|
||||
2 - Committers/developers Git access using SSH
|
||||
3 - Merging a development branch to master branch
|
||||
4 - How to release lwIP
|
||||
|
||||
|
||||
1 Anonymous CVS checkouts and updates
|
||||
-------------------------------------
|
||||
|
||||
Obtain lwIP from the STABLE tree (on the CVS main trunk)
|
||||
1 Obtaining lwIP from the Git repository
|
||||
----------------------------------------
|
||||
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip login
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip checkout lwip
|
||||
To perform an anonymous Git clone of the master branch (this is where
|
||||
bug fixes and incremental enhancements occur), do this:
|
||||
git clone git://git.savannah.nongnu.org/lwip.git
|
||||
|
||||
Or, obtain a specific release as follows:
|
||||
Or, obtain a stable branch (updated with bug fixes only) as follows:
|
||||
git clone --branch DEVEL-1_4_1 git://git.savannah.nongnu.org/lwip.git
|
||||
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip login
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip checkout -r STABLE-0_6_3 -d lwip-0.6.3 lwip
|
||||
|
||||
Or, obtain a development branch as follows:
|
||||
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip login
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip checkout -r DEVEL -d lwip-devel lwip
|
||||
Or, obtain a specific (fixed) release as follows:
|
||||
git clone --branch STABLE-1_4_1 git://git.savannah.nongnu.org/lwip.git
|
||||
|
||||
|
||||
3 Merging from DEVEL branch to main trunk (stable branch)
|
||||
---------------------------------------------------------
|
||||
2 Committers/developers Git access using SSH
|
||||
--------------------------------------------
|
||||
|
||||
Merging from DEVEL to main requires that the DEVEL branch
|
||||
has a tag indicating the previous merger. This tag is
|
||||
called 'merged_from_DEVEL_to_main'.
|
||||
The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption.
|
||||
As such, Git commits to the server occur through a SSH tunnel for project members.
|
||||
To create a SSH2 key pair in UNIX-like environments, do this:
|
||||
ssh-keygen -t dsa
|
||||
|
||||
IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE
|
||||
REPOSITORY, THE TAG MUST BE SET ON THE SOURCE OF THE
|
||||
MERGE BRANCH (REPLACING EXISTING TAGS WITH THE SAME NAME).
|
||||
Under Windows, a recommended SSH client is "PuTTY", freely available with good
|
||||
documentation and a graphic user interface. Use its key generator.
|
||||
|
||||
Merge all changes in DEVEL since our last merge to main:
|
||||
Now paste the id_dsa.pub contents into your Savannah account public key list. Wait
|
||||
a while so that Savannah can update its configuration (This can take minutes).
|
||||
|
||||
In the working copy of the main trunk:
|
||||
cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL
|
||||
Try to login using SSH:
|
||||
ssh -v your_login@git.sv.gnu.org
|
||||
|
||||
Now move the tag in the DEVEL branch to this merge point,
|
||||
so we can use this for future merges.
|
||||
If it tells you:
|
||||
Linux vcs.savannah.gnu.org 2.6.32-5-xen-686 #1 SMP Wed Jun 17 17:10:03 UTC 2015 i686
|
||||
|
||||
Interactive shell login is not possible for security reasons.
|
||||
VCS commands are allowed.
|
||||
Last login: Tue May 15 23:10:12 2012 from 82.245.102.129
|
||||
You tried to execute:
|
||||
Sorry, you are not allowed to execute that command.
|
||||
Shared connection to git.sv.gnu.org closed.
|
||||
|
||||
then you could login; Savannah refuses to give you a shell - which is OK, as we
|
||||
are allowed to use SSH for Git only. Now, you should be able to do this:
|
||||
git clone your_login@git.sv.gnu.org:/srv/git/lwip.git
|
||||
|
||||
After which you can edit your local files with bug fixes or new features and
|
||||
commit them. Make sure you know what you are doing when using Git to make
|
||||
changes on the repository. If in doubt, ask on the lwip-members mailing list.
|
||||
|
||||
(If SSH asks about authenticity of the host, you can check the key
|
||||
fingerprint against https://savannah.nongnu.org/git/?group=lwip
|
||||
|
||||
|
||||
3 - Merging a development branch to master branch
|
||||
-------------------------------------------------
|
||||
|
||||
Merging is a straightforward process in Git. How to merge all changes in a
|
||||
development branch since our last merge from main:
|
||||
|
||||
Checkout the master branch:
|
||||
git checkout master
|
||||
|
||||
Merge the development branch to master:
|
||||
git merge your-development-branch
|
||||
|
||||
Resolve any conflict.
|
||||
|
||||
Commit the merge result.
|
||||
git commit -a
|
||||
|
||||
Push your commits:
|
||||
git push
|
||||
|
||||
cvs -z4 rtag -F -r DEVEL merged_from_DEVEL_to_main lwip
|
||||
|
||||
4 How to release lwIP
|
||||
---------------------
|
||||
|
||||
First, checkout a clean copy of the branch to be released. Tag this set with
|
||||
tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example).
|
||||
First, tag the release using Git: (I use release number 1.4.1 throughout
|
||||
this example).
|
||||
git tag -a STABLE-1_4_1
|
||||
|
||||
Login CVS using pserver authentication, then export a clean copy of the
|
||||
tagged tree. Export is similar to a checkout, except that the CVS metadata
|
||||
is not created locally.
|
||||
Share the tag reference by pushing it to remote:
|
||||
git push origin STABLE-1_4_1
|
||||
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip login
|
||||
cvs -d:pserver:anoncvs@subversions.gnu.org:/cvsroot/lwip export -r STABLE-0_6_3 -d lwip-0.6.3 lwip
|
||||
Prepare the release:
|
||||
cp -r lwip lwip-1.4.1
|
||||
rm -rf lwip-1.4.1/.git lwip-1.4.1/.gitattributes
|
||||
|
||||
Archive this directory using tar.
|
||||
Archive the current directory using tar, gzip'd, bzip2'd and zip'd.
|
||||
tar czvf lwip-1.4.1.tar.gz lwip-1.4.1
|
||||
tar cjvf lwip-1.4.1.tar.bz2 lwip-1.4.1
|
||||
zip -r lwip-1.4.1.zip lwip-1.4.1
|
||||
|
||||
tar czvf lwip-0.6.3.tar.gz lwip-0.6.3
|
||||
Now, sign the archives with a detached GPG binary signature as follows:
|
||||
gpg -b lwip-1.4.1.tar.gz
|
||||
gpg -b lwip-1.4.1.tar.bz2
|
||||
gpg -b lwip-1.4.1.zip
|
||||
|
||||
First, make a local release directory to work in, I use "lwip-releases":
|
||||
|
||||
mkdir lwip-releases
|
||||
cd lwip-releases
|
||||
|
||||
Now, make a new release by creating a new directory for it (these are
|
||||
Savannah conventions so that it shows up in the Files list real nice):
|
||||
|
||||
mkdir stable.pkg
|
||||
cd stable.pkg
|
||||
mkdir 0.6.3
|
||||
cd 0.6.3
|
||||
|
||||
We can now copy the tar archive we made earlier into the release directory:
|
||||
|
||||
cp /../../../lwip-0.6.3.tar.gz .
|
||||
|
||||
Finally, synchronize this directory upwards to Savannah:
|
||||
|
||||
rsync -n -e "ssh -1" -t -u -v -r . likewise@savannah.nongnu.org:/upload/lwip
|
||||
|
||||
This does a "dry run": no files are modified! After you have confirmed that
|
||||
this is what you intended to do, remove "-n" and actually synchronize for
|
||||
real. The release should now be available here:
|
||||
|
||||
http://savannah.nongnu.org/files/?group=lwip
|
||||
|
||||
---
|
||||
Explanation of rsync options used:
|
||||
|
||||
-t: preserve file timestamps
|
||||
-u: do not overwrite existing files, unless they are older
|
||||
-v: be verbose (long format file attributes)
|
||||
-r: recurse into directories
|
||||
-n: dry-run, do not modify anything.
|
||||
---
|
||||
Upload these files using anonymous FTP:
|
||||
ncftp ftp://savannah.gnu.org/incoming/savannah/lwip
|
||||
ncftp> mput *1.4.1.*
|
||||
|
||||
Additionally, you may post a news item on Savannah, like this:
|
||||
|
||||
A new 0.6.3 release is now available here:
|
||||
http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3
|
||||
A new 1.4.1 release is now available here:
|
||||
http://savannah.nongnu.org/files/?group=lwip&highlight=1.4.1
|
||||
|
||||
You will have to submit this via the user News interface, then approve
|
||||
this via the Administrator News interface.
|
||||
this via the Administrator News interface.
|
||||
|
||||
195
doc/sys_arch.txt
195
doc/sys_arch.txt
@@ -1,6 +1,7 @@
|
||||
sys_arch interface for lwIP 0.6++
|
||||
sys_arch interface for lwIP
|
||||
|
||||
Author: Adam Dunkels
|
||||
Simon Goldschmidt
|
||||
|
||||
The operating system emulation layer provides a common interface
|
||||
between the lwIP code and the underlying operating system kernel. The
|
||||
@@ -9,12 +10,11 @@ small changes to a few header files and a new sys_arch
|
||||
implementation. It is also possible to do a sys_arch implementation
|
||||
that does not rely on any underlying operating system.
|
||||
|
||||
The sys_arch provides semaphores and mailboxes to lwIP. For the full
|
||||
The sys_arch provides semaphores, mailboxes and mutexes to lwIP. For the full
|
||||
lwIP functionality, multiple threads support can be implemented in the
|
||||
sys_arch, but this is not required for the basic lwIP
|
||||
functionality. Previous versions of lwIP required the sys_arch to
|
||||
implement timer scheduling as well but as of lwIP 0.5 this is
|
||||
implemented in a higher layer.
|
||||
functionality. Timer scheduling is implemented in lwIP, but can be implemented
|
||||
by the sys_arch port (LWIP_TIMERS_CUSTOM==1).
|
||||
|
||||
In addition to the source file providing the functionality of sys_arch,
|
||||
the OS emulation layer must provide several header files defining
|
||||
@@ -22,17 +22,21 @@ macros used throughout lwip. The files required and the macros they
|
||||
must define are listed below the sys_arch description.
|
||||
|
||||
Semaphores can be either counting or binary - lwIP works with both
|
||||
kinds. Mailboxes are used for message passing and can be implemented
|
||||
either as a queue which allows multiple messages to be posted to a
|
||||
mailbox, or as a rendez-vous point where only one message can be
|
||||
posted at a time. lwIP works with both kinds, but the former type will
|
||||
be more efficient. A message in a mailbox is just a pointer, nothing
|
||||
more.
|
||||
kinds. Mailboxes should be implemented as a queue which allows multiple messages
|
||||
to be posted (implementing as a rendez-vous point where only one message can be
|
||||
posted at a time can have a highly negative impact on performance). A message
|
||||
in a mailbox is just a pointer, nothing more.
|
||||
|
||||
Semaphores are represented by the type "sys_sem_t" which is typedef'd
|
||||
in the sys_arch.h file. Mailboxes are equivalently represented by the
|
||||
type "sys_mbox_t". lwIP does not place any restrictions on how
|
||||
sys_sem_t or sys_mbox_t are represented internally.
|
||||
type "sys_mbox_t". Mutexes are represented ny the type "sys_mutex_t".
|
||||
lwIP does not place any restrictions on how these types are represented
|
||||
internally.
|
||||
|
||||
Since lwIP 1.4.0, semaphore, mutexes and mailbox functions are prototyped in a way that
|
||||
allows both using pointers or actual OS structures to be used. This way, memory
|
||||
required for such types can be either allocated in place (globally or on the
|
||||
stack) or on the heap (allocated internally in the "*_new()" functions).
|
||||
|
||||
The following functions must be implemented by the sys_arch:
|
||||
|
||||
@@ -40,25 +44,31 @@ The following functions must be implemented by the sys_arch:
|
||||
|
||||
Is called to initialize the sys_arch layer.
|
||||
|
||||
- sys_sem_t sys_sem_new(u8_t count)
|
||||
- err_t sys_sem_new(sys_sem_t *sem, u8_t count)
|
||||
|
||||
Creates and returns a new semaphore. The "count" argument specifies
|
||||
the initial state of the semaphore.
|
||||
Creates a new semaphore. The semaphore is allocated to the memory that 'sem'
|
||||
points to (which can be both a pointer or the actual OS structure).
|
||||
The "count" argument specifies the initial state of the semaphore (which is
|
||||
either 0 or 1).
|
||||
If the semaphore has been created, ERR_OK should be returned. Returning any
|
||||
other error will provide a hint what went wrong, but except for assertions,
|
||||
no real error handling is implemented.
|
||||
|
||||
- void sys_sem_free(sys_sem_t sem)
|
||||
- void sys_sem_free(sys_sem_t *sem)
|
||||
|
||||
Deallocates a semaphore.
|
||||
|
||||
- void sys_sem_signal(sys_sem_t sem)
|
||||
- void sys_sem_signal(sys_sem_t *sem)
|
||||
|
||||
Signals a semaphore.
|
||||
|
||||
- u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)
|
||||
- u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
|
||||
|
||||
Blocks the thread while waiting for the semaphore to be
|
||||
signaled. If the "timeout" argument is non-zero, the thread should
|
||||
only be blocked for the specified time (measured in
|
||||
milliseconds).
|
||||
milliseconds). If the "timeout" argument is zero, the thread should be
|
||||
blocked until the semaphore is signalled.
|
||||
|
||||
If the timeout argument is non-zero, the return value is the number of
|
||||
milliseconds spent waiting for the semaphore to be signaled. If the
|
||||
@@ -69,25 +79,86 @@ The following functions must be implemented by the sys_arch:
|
||||
Notice that lwIP implements a function with a similar name,
|
||||
sys_sem_wait(), that uses the sys_arch_sem_wait() function.
|
||||
|
||||
- sys_mbox_t sys_mbox_new(void)
|
||||
- int sys_sem_valid(sys_sem_t *sem)
|
||||
|
||||
Creates an empty mailbox.
|
||||
Returns 1 if the semaphore is valid, 0 if it is not valid.
|
||||
When using pointers, a simple way is to check the pointer for != NULL.
|
||||
When directly using OS structures, implementing this may be more complex.
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
- void sys_mbox_free(sys_mbox_t mbox)
|
||||
- void sys_sem_set_invalid(sys_sem_t *sem)
|
||||
|
||||
Invalidate a semaphore so that sys_sem_valid() returns 0.
|
||||
ATTENTION: This does NOT mean that the semaphore shall be deallocated:
|
||||
sys_sem_free() is always called before calling this function!
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
- void sys_mutex_new(sys_mutex_t *mutex)
|
||||
|
||||
Creates a new mutex. The mutex is allocated to the memory that 'mutex'
|
||||
points to (which can be both a pointer or the actual OS structure).
|
||||
If the mutex has been created, ERR_OK should be returned. Returning any
|
||||
other error will provide a hint what went wrong, but except for assertions,
|
||||
no real error handling is implemented.
|
||||
|
||||
- void sys_mutex_free(sys_mutex_t *mutex)
|
||||
|
||||
Deallocates a mutex.
|
||||
|
||||
- void sys_mutex_lock(sys_mutex_t *mutex)
|
||||
|
||||
Blocks the thread until the mutex can be grabbed.
|
||||
|
||||
- void sys_mutex_unlock(sys_mutex_t *mutex)
|
||||
|
||||
Releases the mutex previously locked through 'sys_mutex_lock()'.
|
||||
|
||||
- void sys_mutex_valid(sys_mutex_t *mutex)
|
||||
|
||||
Returns 1 if the mutes is valid, 0 if it is not valid.
|
||||
When using pointers, a simple way is to check the pointer for != NULL.
|
||||
When directly using OS structures, implementing this may be more complex.
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
- void sys_mutex_set_invalid(sys_mutex_t *mutex)
|
||||
|
||||
Invalidate a mutex so that sys_mutex_valid() returns 0.
|
||||
ATTENTION: This does NOT mean that the mutex shall be deallocated:
|
||||
sys_mutex_free() is always called before calling this function!
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
- err_t sys_mbox_new(sys_mbox_t *mbox, int size)
|
||||
|
||||
Creates an empty mailbox for maximum "size" elements. Elements stored
|
||||
in mailboxes are pointers. You have to define macros "_MBOX_SIZE"
|
||||
in your lwipopts.h, or ignore this parameter in your implementation
|
||||
and use a default size.
|
||||
If the mailbox has been created, ERR_OK should be returned. Returning any
|
||||
other error will provide a hint what went wrong, but except for assertions,
|
||||
no real error handling is implemented.
|
||||
|
||||
- void sys_mbox_free(sys_mbox_t *mbox)
|
||||
|
||||
Deallocates a mailbox. If there are messages still present in the
|
||||
mailbox when the mailbox is deallocated, it is an indication of a
|
||||
programming error in lwIP and the developer should be notified.
|
||||
|
||||
- void sys_mbox_post(sys_mbox_t mbox, void *msg)
|
||||
- void sys_mbox_post(sys_mbox_t *mbox, void *msg)
|
||||
|
||||
Posts the "msg" to the mailbox.
|
||||
Posts the "msg" to the mailbox. This function have to block until
|
||||
the "msg" is really posted.
|
||||
|
||||
- u32_t sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout)
|
||||
- err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
|
||||
|
||||
Try to post the "msg" to the mailbox. Returns ERR_MEM if this one
|
||||
is full, else, ERR_OK if the "msg" is posted.
|
||||
|
||||
- u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
|
||||
|
||||
Blocks the thread until a message arrives in the mailbox, but does
|
||||
not block the thread longer than "timeout" milliseconds (similar to
|
||||
the sys_arch_sem_wait() function). The "msg" argument is a result
|
||||
the sys_arch_sem_wait() function). If "timeout" is 0, the thread should
|
||||
be blocked until a message arrives. The "msg" argument is a result
|
||||
parameter that is set by the function (i.e., by doing "*msg =
|
||||
ptr"). The "msg" parameter maybe NULL to indicate that the message
|
||||
should be dropped.
|
||||
@@ -98,29 +169,48 @@ The following functions must be implemented by the sys_arch:
|
||||
|
||||
Note that a function with a similar name, sys_mbox_fetch(), is
|
||||
implemented by lwIP.
|
||||
|
||||
- struct sys_timeouts *sys_arch_timeouts(void)
|
||||
|
||||
Returns a pointer to the per-thread sys_timeouts structure. In lwIP,
|
||||
each thread has a list of timeouts which is repressented as a linked
|
||||
list of sys_timeout structures. The sys_timeouts structure holds a
|
||||
pointer to a linked list of timeouts. This function is called by
|
||||
the lwIP timeout scheduler and must not return a NULL value.
|
||||
- u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
|
||||
|
||||
This is similar to sys_arch_mbox_fetch, however if a message is not
|
||||
present in the mailbox, it immediately returns with the code
|
||||
SYS_MBOX_EMPTY. On success 0 is returned.
|
||||
|
||||
To allow for efficient implementations, this can be defined as a
|
||||
function-like macro in sys_arch.h instead of a normal function. For
|
||||
example, a naive implementation could be:
|
||||
#define sys_arch_mbox_tryfetch(mbox,msg) \
|
||||
sys_arch_mbox_fetch(mbox,msg,1)
|
||||
although this would introduce unnecessary delays.
|
||||
|
||||
- int sys_mbox_valid(sys_mbox_t *mbox)
|
||||
|
||||
Returns 1 if the mailbox is valid, 0 if it is not valid.
|
||||
When using pointers, a simple way is to check the pointer for != NULL.
|
||||
When directly using OS structures, implementing this may be more complex.
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
- void sys_mbox_set_invalid(sys_mbox_t *mbox)
|
||||
|
||||
Invalidate a mailbox so that sys_mbox_valid() returns 0.
|
||||
ATTENTION: This does NOT mean that the mailbox shall be deallocated:
|
||||
sys_mbox_free() is always called before calling this function!
|
||||
This may also be a define, in which case the function is not prototyped.
|
||||
|
||||
In a single threadd sys_arch implementation, this function will
|
||||
simply return a pointer to a global sys_timeouts variable stored in
|
||||
the sys_arch module.
|
||||
|
||||
If threads are supported by the underlying operating system and if
|
||||
such functionality is needed in lwIP, the following function will have
|
||||
to be implemented as well:
|
||||
|
||||
- sys_thread_t sys_thread_new(void (* thread)(void *arg), void *arg, int prio)
|
||||
- sys_thread_t sys_thread_new(char *name, void (* thread)(void *arg), void *arg, int stacksize, int prio)
|
||||
|
||||
Starts a new thread with priority "prio" that will begin its execution in the
|
||||
function "thread()". The "arg" argument will be passed as an argument to the
|
||||
thread() function. The id of the new thread is returned. Both the id and
|
||||
the priority are system dependent.
|
||||
Starts a new thread named "name" with priority "prio" that will begin its
|
||||
execution in the function "thread()". The "arg" argument will be passed as an
|
||||
argument to the thread() function. The stack size to used for this thread is
|
||||
the "stacksize" parameter. The id of the new thread is returned. Both the id
|
||||
and the priority are system dependent.
|
||||
|
||||
When lwIP is used from more than one context (e.g. from multiple threads OR from
|
||||
main-loop and from interrupts), the SYS_LIGHTWEIGHT_PROT protection SHOULD be enabled!
|
||||
|
||||
- sys_prot_t sys_arch_protect(void)
|
||||
|
||||
@@ -143,6 +233,23 @@ to be implemented as well:
|
||||
more information. This function is only required if your port is supporting
|
||||
an operating system.
|
||||
|
||||
For some configurations, you also need:
|
||||
|
||||
- u32_t sys_now(void)
|
||||
|
||||
This optional function returns the current time in milliseconds (don't care
|
||||
for wraparound, this is only used for time diffs).
|
||||
Not implementing this function means you cannot use some modules (e.g. TCP
|
||||
timestamps, internal timeouts for NO_SYS==1).
|
||||
|
||||
|
||||
Note:
|
||||
|
||||
Be careful with using mem_malloc() in sys_arch. When malloc() refers to
|
||||
mem_malloc() you can run into a circular function call problem. In mem.c
|
||||
mem_init() tries to allcate a semaphore using mem_malloc, which of course
|
||||
can't be performed when sys_arch uses mem_malloc.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Additional files required for the "OS support" emulation layer:
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -163,6 +270,8 @@ cc.h - Architecture environment, some compiler specific, some
|
||||
Platform specific diagnostic output -
|
||||
LWIP_PLATFORM_DIAG(x) - non-fatal, print a message.
|
||||
LWIP_PLATFORM_ASSERT(x) - fatal, print message and abandon execution.
|
||||
Portability defines for printf formatters:
|
||||
U16_F, S16_F, X16_F, U32_F, S32_F, X32_F, SZT_F
|
||||
|
||||
"lightweight" synchronization mechanisms -
|
||||
SYS_ARCH_DECL_PROTECT(x) - declare a protection state variable.
|
||||
|
||||
12
src/FILES
12
src/FILES
@@ -1,8 +1,12 @@
|
||||
api/ - The code for the API.
|
||||
api/ - The code for the high-level wrapper API. Not needed if
|
||||
you use the lowel-level call-back/raw API.
|
||||
|
||||
apps/ - Higher layer applications that are specifically programmed
|
||||
with the lwIP low-level raw API.
|
||||
|
||||
core/ - The core of the TPC/IP stack; protocol implementations,
|
||||
memory and buffer management, and the low-level raw API.
|
||||
|
||||
core/ - The core files including protocol implementations, memory
|
||||
and buffer management etc.
|
||||
|
||||
include/ - lwIP include files.
|
||||
|
||||
netif/ - Generic network interface device drivers are kept here.
|
||||
|
||||
177
src/Filelists.mk
Normal file
177
src/Filelists.mk
Normal file
@@ -0,0 +1,177 @@
|
||||
#
|
||||
# Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
# SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
# OF SUCH DAMAGE.
|
||||
#
|
||||
# This file is part of the lwIP TCP/IP stack.
|
||||
#
|
||||
# Author: Adam Dunkels <adam@sics.se>
|
||||
#
|
||||
|
||||
# COREFILES, CORE4FILES: The minimum set of files needed for lwIP.
|
||||
COREFILES=$(LWIPDIR)/core/init.c \
|
||||
$(LWIPDIR)/core/def.c \
|
||||
$(LWIPDIR)/core/dns.c \
|
||||
$(LWIPDIR)/core/inet_chksum.c \
|
||||
$(LWIPDIR)/core/ip.c \
|
||||
$(LWIPDIR)/core/mem.c \
|
||||
$(LWIPDIR)/core/memp.c \
|
||||
$(LWIPDIR)/core/netif.c \
|
||||
$(LWIPDIR)/core/pbuf.c \
|
||||
$(LWIPDIR)/core/raw.c \
|
||||
$(LWIPDIR)/core/stats.c \
|
||||
$(LWIPDIR)/core/sys.c \
|
||||
$(LWIPDIR)/core/tcp.c \
|
||||
$(LWIPDIR)/core/tcp_in.c \
|
||||
$(LWIPDIR)/core/tcp_out.c \
|
||||
$(LWIPDIR)/core/timeouts.c \
|
||||
$(LWIPDIR)/core/udp.c
|
||||
|
||||
CORE4FILES=$(LWIPDIR)/core/ipv4/autoip.c \
|
||||
$(LWIPDIR)/core/ipv4/dhcp.c \
|
||||
$(LWIPDIR)/core/ipv4/etharp.c \
|
||||
$(LWIPDIR)/core/ipv4/icmp.c \
|
||||
$(LWIPDIR)/core/ipv4/igmp.c \
|
||||
$(LWIPDIR)/core/ipv4/ip4_frag.c \
|
||||
$(LWIPDIR)/core/ipv4/ip4.c \
|
||||
$(LWIPDIR)/core/ipv4/ip4_addr.c
|
||||
|
||||
CORE6FILES=$(LWIPDIR)/core/ipv6/dhcp6.c \
|
||||
$(LWIPDIR)/core/ipv6/ethip6.c \
|
||||
$(LWIPDIR)/core/ipv6/icmp6.c \
|
||||
$(LWIPDIR)/core/ipv6/inet6.c \
|
||||
$(LWIPDIR)/core/ipv6/ip6.c \
|
||||
$(LWIPDIR)/core/ipv6/ip6_addr.c \
|
||||
$(LWIPDIR)/core/ipv6/ip6_frag.c \
|
||||
$(LWIPDIR)/core/ipv6/mld6.c \
|
||||
$(LWIPDIR)/core/ipv6/nd6.c
|
||||
|
||||
# APIFILES: The files which implement the sequential and socket APIs.
|
||||
APIFILES=$(LWIPDIR)/api/api_lib.c \
|
||||
$(LWIPDIR)/api/api_msg.c \
|
||||
$(LWIPDIR)/api/err.c \
|
||||
$(LWIPDIR)/api/netbuf.c \
|
||||
$(LWIPDIR)/api/netdb.c \
|
||||
$(LWIPDIR)/api/netifapi.c \
|
||||
$(LWIPDIR)/api/sockets.c \
|
||||
$(LWIPDIR)/api/tcpip.c
|
||||
|
||||
# NETIFFILES: Files implementing various generic network interface functions
|
||||
NETIFFILES=$(LWIPDIR)/netif/ethernet.c \
|
||||
$(LWIPDIR)/netif/slipif.c
|
||||
|
||||
# SIXLOWPAN: 6LoWPAN
|
||||
SIXLOWPAN=$(LWIPDIR)/netif/lowpan6.c \
|
||||
|
||||
# PPPFILES: PPP
|
||||
PPPFILES=$(LWIPDIR)/netif/ppp/auth.c \
|
||||
$(LWIPDIR)/netif/ppp/ccp.c \
|
||||
$(LWIPDIR)/netif/ppp/chap-md5.c \
|
||||
$(LWIPDIR)/netif/ppp/chap_ms.c \
|
||||
$(LWIPDIR)/netif/ppp/chap-new.c \
|
||||
$(LWIPDIR)/netif/ppp/demand.c \
|
||||
$(LWIPDIR)/netif/ppp/eap.c \
|
||||
$(LWIPDIR)/netif/ppp/ecp.c \
|
||||
$(LWIPDIR)/netif/ppp/eui64.c \
|
||||
$(LWIPDIR)/netif/ppp/fsm.c \
|
||||
$(LWIPDIR)/netif/ppp/ipcp.c \
|
||||
$(LWIPDIR)/netif/ppp/ipv6cp.c \
|
||||
$(LWIPDIR)/netif/ppp/lcp.c \
|
||||
$(LWIPDIR)/netif/ppp/magic.c \
|
||||
$(LWIPDIR)/netif/ppp/mppe.c \
|
||||
$(LWIPDIR)/netif/ppp/multilink.c \
|
||||
$(LWIPDIR)/netif/ppp/ppp.c \
|
||||
$(LWIPDIR)/netif/ppp/pppapi.c \
|
||||
$(LWIPDIR)/netif/ppp/pppcrypt.c \
|
||||
$(LWIPDIR)/netif/ppp/pppoe.c \
|
||||
$(LWIPDIR)/netif/ppp/pppol2tp.c \
|
||||
$(LWIPDIR)/netif/ppp/pppos.c \
|
||||
$(LWIPDIR)/netif/ppp/upap.c \
|
||||
$(LWIPDIR)/netif/ppp/utils.c \
|
||||
$(LWIPDIR)/netif/ppp/vj.c \
|
||||
$(LWIPDIR)/netif/ppp/polarssl/arc4.c \
|
||||
$(LWIPDIR)/netif/ppp/polarssl/des.c \
|
||||
$(LWIPDIR)/netif/ppp/polarssl/md4.c \
|
||||
$(LWIPDIR)/netif/ppp/polarssl/md5.c \
|
||||
$(LWIPDIR)/netif/ppp/polarssl/sha1.c
|
||||
|
||||
# LWIPNOAPPSFILES: All LWIP files without apps
|
||||
LWIPNOAPPSFILES=$(COREFILES) \
|
||||
$(CORE4FILES) \
|
||||
$(CORE6FILES) \
|
||||
$(APIFILES) \
|
||||
$(NETIFFILES) \
|
||||
$(PPPFILES) \
|
||||
$(SIXLOWPAN)
|
||||
|
||||
# SNMPFILES: SNMPv2c agent
|
||||
SNMPFILES=$(LWIPDIR)/apps/snmp/snmp_asn1.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_core.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_icmp.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_interfaces.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_ip.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_snmp.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_system.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_tcp.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_mib2_udp.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_msg.c \
|
||||
$(LWIPDIR)/apps/snmp/snmpv3.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_netconn.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_pbuf_stream.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_raw.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_scalar.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_table.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_threadsync.c \
|
||||
$(LWIPDIR)/apps/snmp/snmp_traps.c \
|
||||
$(LWIPDIR)/apps/snmp/snmpv3_mbedtls.c \
|
||||
$(LWIPDIR)/apps/snmp/snmpv3_dummy.c
|
||||
|
||||
# HTTPDFILES: HTTP server
|
||||
HTTPDFILES=$(LWIPDIR)/apps/httpd/fs.c \
|
||||
$(LWIPDIR)/apps/httpd/httpd.c
|
||||
|
||||
# LWIPERFFILES: IPERF server
|
||||
LWIPERFFILES=$(LWIPDIR)/apps/lwiperf/lwiperf.c
|
||||
|
||||
# SNTPFILES: SNTP client
|
||||
SNTPFILES=$(LWIPDIR)/apps/sntp/sntp.c
|
||||
|
||||
# MDNSFILES: MDNS responder
|
||||
MDNSFILES=$(LWIPDIR)/apps/mdns/mdns.c
|
||||
|
||||
# NETBIOSNSFILES: NetBIOS name server
|
||||
NETBIOSNSFILES=$(LWIPDIR)/apps/netbiosns/netbiosns.c
|
||||
|
||||
# TFTPFILES: TFTP server files
|
||||
TFTPFILES=$(LWIPDIR)/apps/tftp/tftp_server.c
|
||||
|
||||
# LWIPAPPFILES: All LWIP APPs
|
||||
LWIPAPPFILES=$(SNMPFILES) \
|
||||
$(HTTPDFILES) \
|
||||
$(LWIPERFFILES) \
|
||||
$(SNTPFILES) \
|
||||
$(MDNSFILES) \
|
||||
$(NETBIOSNSFILES) \
|
||||
$(TFTPFILES)
|
||||
1487
src/api/api_lib.c
1487
src/api/api_lib.c
File diff suppressed because it is too large
Load Diff
2303
src/api/api_msg.c
2303
src/api/api_msg.c
File diff suppressed because it is too large
Load Diff
118
src/api/err.c
118
src/api/err.c
@@ -1,8 +1,14 @@
|
||||
/**
|
||||
* @file
|
||||
* Error Management module
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
@@ -11,49 +17,101 @@
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include "lwip/errno.h"
|
||||
|
||||
#if !NO_SYS
|
||||
/** Table to quickly map an lwIP error (err_t) to a socket error
|
||||
* by using -err as an index */
|
||||
static const int err_to_errno_table[] = {
|
||||
0, /* ERR_OK 0 No error, everything OK. */
|
||||
ENOMEM, /* ERR_MEM -1 Out of memory error. */
|
||||
ENOBUFS, /* ERR_BUF -2 Buffer error. */
|
||||
EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
|
||||
EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
|
||||
EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
|
||||
EINVAL, /* ERR_VAL -6 Illegal value. */
|
||||
EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
|
||||
EADDRINUSE, /* ERR_USE -8 Address in use. */
|
||||
EALREADY, /* ERR_ALREADY -9 Already connecting. */
|
||||
EISCONN, /* ERR_ISCONN -10 Conn already established.*/
|
||||
ENOTCONN, /* ERR_CONN -11 Not connected. */
|
||||
-1, /* ERR_IF -12 Low-level netif error */
|
||||
ECONNABORTED, /* ERR_ABRT -13 Connection aborted. */
|
||||
ECONNRESET, /* ERR_RST -14 Connection reset. */
|
||||
ENOTCONN, /* ERR_CLSD -15 Connection closed. */
|
||||
EIO /* ERR_ARG -16 Illegal argument. */
|
||||
};
|
||||
#endif /* !NO_SYS */
|
||||
|
||||
#ifdef LWIP_DEBUG
|
||||
|
||||
static char *err_strerr[] = {"Ok.",
|
||||
"Out of memory error.",
|
||||
"Buffer error.",
|
||||
"Connection aborted.",
|
||||
"Connection reset.",
|
||||
"Connection closed.",
|
||||
"Not connected.",
|
||||
"Illegal value.",
|
||||
"Illegal argument.",
|
||||
"Routing problem.",
|
||||
"Address in use."
|
||||
static const char *err_strerr[] = {
|
||||
"Ok.", /* ERR_OK 0 */
|
||||
"Out of memory error.", /* ERR_MEM -1 */
|
||||
"Buffer error.", /* ERR_BUF -2 */
|
||||
"Timeout.", /* ERR_TIMEOUT -3 */
|
||||
"Routing problem.", /* ERR_RTE -4 */
|
||||
"Operation in progress.", /* ERR_INPROGRESS -5 */
|
||||
"Illegal value.", /* ERR_VAL -6 */
|
||||
"Operation would block.", /* ERR_WOULDBLOCK -7 */
|
||||
"Address in use.", /* ERR_USE -8 */
|
||||
"Already connecting.", /* ERR_ALREADY -9 */
|
||||
"Already connected.", /* ERR_ISCONN -10 */
|
||||
"Not connected.", /* ERR_CONN -11 */
|
||||
"Low-level netif error.", /* ERR_IF -12 */
|
||||
"Connection aborted.", /* ERR_ABRT -13 */
|
||||
"Connection reset.", /* ERR_RST -14 */
|
||||
"Connection closed.", /* ERR_CLSD -15 */
|
||||
"Illegal argument." /* ERR_ARG -16 */
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
char *
|
||||
/**
|
||||
* Convert an lwip internal error to a string representation.
|
||||
*
|
||||
* @param err an lwip internal err_t
|
||||
* @return a string representation for err
|
||||
*/
|
||||
const char *
|
||||
lwip_strerr(err_t err)
|
||||
{
|
||||
if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_strerr))) {
|
||||
return "Unknown error.";
|
||||
}
|
||||
return err_strerr[-err];
|
||||
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
#endif /* LWIP_DEBUG */
|
||||
|
||||
#if !NO_SYS
|
||||
int
|
||||
err_to_errno(err_t err)
|
||||
{
|
||||
if ((err > 0) || (-err >= (err_t)LWIP_ARRAYSIZE(err_to_errno_table))) {
|
||||
return EIO;
|
||||
}
|
||||
return err_to_errno_table[-err];
|
||||
}
|
||||
#endif /* !NO_SYS */
|
||||
|
||||
246
src/api/netbuf.c
Normal file
246
src/api/netbuf.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* @file
|
||||
* Network buffer management
|
||||
*
|
||||
* @defgroup netbuf Network buffers
|
||||
* @ingroup netconn
|
||||
* Network buffer descriptor for @ref netconn. Based on @ref pbuf internally
|
||||
* to avoid copying data around.\n
|
||||
* Buffers must not be shared accross multiple threads, all functions except
|
||||
* netbuf_new() and netbuf_delete() are not thread-safe.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/netbuf.h"
|
||||
#include "lwip/memp.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Create (allocate) and initialize a new netbuf.
|
||||
* The netbuf doesn't yet contain a packet buffer!
|
||||
*
|
||||
* @return a pointer to a new netbuf
|
||||
* NULL on lack of memory
|
||||
*/
|
||||
struct
|
||||
netbuf *netbuf_new(void)
|
||||
{
|
||||
struct netbuf *buf;
|
||||
|
||||
buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
|
||||
if (buf != NULL) {
|
||||
memset(buf, 0, sizeof(struct netbuf));
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Deallocate a netbuf allocated by netbuf_new().
|
||||
*
|
||||
* @param buf pointer to a netbuf allocated by netbuf_new()
|
||||
*/
|
||||
void
|
||||
netbuf_delete(struct netbuf *buf)
|
||||
{
|
||||
if (buf != NULL) {
|
||||
if (buf->p != NULL) {
|
||||
pbuf_free(buf->p);
|
||||
buf->p = buf->ptr = NULL;
|
||||
}
|
||||
memp_free(MEMP_NETBUF, buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Allocate memory for a packet buffer for a given netbuf.
|
||||
*
|
||||
* @param buf the netbuf for which to allocate a packet buffer
|
||||
* @param size the size of the packet buffer to allocate
|
||||
* @return pointer to the allocated memory
|
||||
* NULL if no memory could be allocated
|
||||
*/
|
||||
void *
|
||||
netbuf_alloc(struct netbuf *buf, u16_t size)
|
||||
{
|
||||
LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
|
||||
|
||||
/* Deallocate any previously allocated memory. */
|
||||
if (buf->p != NULL) {
|
||||
pbuf_free(buf->p);
|
||||
}
|
||||
buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
|
||||
if (buf->p == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
LWIP_ASSERT("check that first pbuf can hold size",
|
||||
(buf->p->len >= size));
|
||||
buf->ptr = buf->p;
|
||||
return buf->p->payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Free the packet buffer included in a netbuf
|
||||
*
|
||||
* @param buf pointer to the netbuf which contains the packet buffer to free
|
||||
*/
|
||||
void
|
||||
netbuf_free(struct netbuf *buf)
|
||||
{
|
||||
LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
|
||||
if (buf->p != NULL) {
|
||||
pbuf_free(buf->p);
|
||||
}
|
||||
buf->p = buf->ptr = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Let a netbuf reference existing (non-volatile) data.
|
||||
*
|
||||
* @param buf netbuf which should reference the data
|
||||
* @param dataptr pointer to the data to reference
|
||||
* @param size size of the data
|
||||
* @return ERR_OK if data is referenced
|
||||
* ERR_MEM if data couldn't be referenced due to lack of memory
|
||||
*/
|
||||
err_t
|
||||
netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
|
||||
{
|
||||
LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
|
||||
if (buf->p != NULL) {
|
||||
pbuf_free(buf->p);
|
||||
}
|
||||
buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
|
||||
if (buf->p == NULL) {
|
||||
buf->ptr = NULL;
|
||||
return ERR_MEM;
|
||||
}
|
||||
((struct pbuf_rom*)buf->p)->payload = dataptr;
|
||||
buf->p->len = buf->p->tot_len = size;
|
||||
buf->ptr = buf->p;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Chain one netbuf to another (@see pbuf_chain)
|
||||
*
|
||||
* @param head the first netbuf
|
||||
* @param tail netbuf to chain after head, freed by this function, may not be reference after returning
|
||||
*/
|
||||
void
|
||||
netbuf_chain(struct netbuf *head, struct netbuf *tail)
|
||||
{
|
||||
LWIP_ERROR("netbuf_chain: invalid head", (head != NULL), return;);
|
||||
LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
|
||||
pbuf_cat(head->p, tail->p);
|
||||
head->ptr = head->p;
|
||||
memp_free(MEMP_NETBUF, tail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Get the data pointer and length of the data inside a netbuf.
|
||||
*
|
||||
* @param buf netbuf to get the data from
|
||||
* @param dataptr pointer to a void pointer where to store the data pointer
|
||||
* @param len pointer to an u16_t where the length of the data is stored
|
||||
* @return ERR_OK if the information was retrieved,
|
||||
* ERR_BUF on error.
|
||||
*/
|
||||
err_t
|
||||
netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
|
||||
{
|
||||
LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
|
||||
LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
|
||||
LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
|
||||
|
||||
if (buf->ptr == NULL) {
|
||||
return ERR_BUF;
|
||||
}
|
||||
*dataptr = buf->ptr->payload;
|
||||
*len = buf->ptr->len;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Move the current data pointer of a packet buffer contained in a netbuf
|
||||
* to the next part.
|
||||
* The packet buffer itself is not modified.
|
||||
*
|
||||
* @param buf the netbuf to modify
|
||||
* @return -1 if there is no next part
|
||||
* 1 if moved to the next part but now there is no next part
|
||||
* 0 if moved to the next part and there are still more parts
|
||||
*/
|
||||
s8_t
|
||||
netbuf_next(struct netbuf *buf)
|
||||
{
|
||||
LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);
|
||||
if (buf->ptr->next == NULL) {
|
||||
return -1;
|
||||
}
|
||||
buf->ptr = buf->ptr->next;
|
||||
if (buf->ptr->next == NULL) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbuf
|
||||
* Move the current data pointer of a packet buffer contained in a netbuf
|
||||
* to the beginning of the packet.
|
||||
* The packet buffer itself is not modified.
|
||||
*
|
||||
* @param buf the netbuf to modify
|
||||
*/
|
||||
void
|
||||
netbuf_first(struct netbuf *buf)
|
||||
{
|
||||
LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);
|
||||
buf->ptr = buf->p;
|
||||
}
|
||||
|
||||
#endif /* LWIP_NETCONN */
|
||||
413
src/api/netdb.c
Normal file
413
src/api/netdb.c
Normal file
@@ -0,0 +1,413 @@
|
||||
/**
|
||||
* @file
|
||||
* API functions for name resolving
|
||||
*
|
||||
* @defgroup netdbapi NETDB API
|
||||
* @ingroup socket
|
||||
*/
|
||||
|
||||
/*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Simon Goldschmidt
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/netdb.h"
|
||||
|
||||
#if LWIP_DNS && LWIP_SOCKET
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/dns.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/** helper struct for gethostbyname_r to access the char* buffer */
|
||||
struct gethostbyname_r_helper {
|
||||
ip_addr_t *addr_list[2];
|
||||
ip_addr_t addr;
|
||||
char *aliases;
|
||||
};
|
||||
|
||||
/** h_errno is exported in netdb.h for access by applications. */
|
||||
#if LWIP_DNS_API_DECLARE_H_ERRNO
|
||||
int h_errno;
|
||||
#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
|
||||
|
||||
/** define "hostent" variables storage: 0 if we use a static (but unprotected)
|
||||
* set of variables for lwip_gethostbyname, 1 if we use a local storage */
|
||||
#ifndef LWIP_DNS_API_HOSTENT_STORAGE
|
||||
#define LWIP_DNS_API_HOSTENT_STORAGE 0
|
||||
#endif
|
||||
|
||||
/** define "hostent" variables storage */
|
||||
#if LWIP_DNS_API_HOSTENT_STORAGE
|
||||
#define HOSTENT_STORAGE
|
||||
#else
|
||||
#define HOSTENT_STORAGE static
|
||||
#endif /* LWIP_DNS_API_STATIC_HOSTENT */
|
||||
|
||||
/**
|
||||
* Returns an entry containing addresses of address family AF_INET
|
||||
* for the host with name name.
|
||||
* Due to dns_gethostbyname limitations, only one address is returned.
|
||||
*
|
||||
* @param name the hostname to resolve
|
||||
* @return an entry containing addresses of address family AF_INET
|
||||
* for the host with name name
|
||||
*/
|
||||
struct hostent*
|
||||
lwip_gethostbyname(const char *name)
|
||||
{
|
||||
err_t err;
|
||||
ip_addr_t addr;
|
||||
|
||||
/* buffer variables for lwip_gethostbyname() */
|
||||
HOSTENT_STORAGE struct hostent s_hostent;
|
||||
HOSTENT_STORAGE char *s_aliases;
|
||||
HOSTENT_STORAGE ip_addr_t s_hostent_addr;
|
||||
HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
|
||||
HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1];
|
||||
|
||||
/* query host IP address */
|
||||
err = netconn_gethostbyname(name, &addr);
|
||||
if (err != ERR_OK) {
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
|
||||
h_errno = HOST_NOT_FOUND;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* fill hostent */
|
||||
s_hostent_addr = addr;
|
||||
s_phostent_addr[0] = &s_hostent_addr;
|
||||
s_phostent_addr[1] = NULL;
|
||||
strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH);
|
||||
s_hostname[DNS_MAX_NAME_LENGTH] = 0;
|
||||
s_hostent.h_name = s_hostname;
|
||||
s_aliases = NULL;
|
||||
s_hostent.h_aliases = &s_aliases;
|
||||
s_hostent.h_addrtype = AF_INET;
|
||||
s_hostent.h_length = sizeof(ip_addr_t);
|
||||
s_hostent.h_addr_list = (char**)&s_phostent_addr;
|
||||
|
||||
#if DNS_DEBUG
|
||||
/* dump hostent */
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name));
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", (void*)s_hostent.h_aliases));
|
||||
/* h_aliases are always empty */
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype));
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length));
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", (void*)s_hostent.h_addr_list));
|
||||
if (s_hostent.h_addr_list != NULL) {
|
||||
u8_t idx;
|
||||
for (idx=0; s_hostent.h_addr_list[idx]; idx++) {
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx]));
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ipaddr_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
|
||||
}
|
||||
}
|
||||
#endif /* DNS_DEBUG */
|
||||
|
||||
#if LWIP_DNS_API_HOSTENT_STORAGE
|
||||
/* this function should return the "per-thread" hostent after copy from s_hostent */
|
||||
return sys_thread_hostent(&s_hostent);
|
||||
#else
|
||||
return &s_hostent;
|
||||
#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread-safe variant of lwip_gethostbyname: instead of using a static
|
||||
* buffer, this function takes buffer and errno pointers as arguments
|
||||
* and uses these for the result.
|
||||
*
|
||||
* @param name the hostname to resolve
|
||||
* @param ret pre-allocated struct where to store the result
|
||||
* @param buf pre-allocated buffer where to store additional data
|
||||
* @param buflen the size of buf
|
||||
* @param result pointer to a hostent pointer that is set to ret on success
|
||||
* and set to zero on error
|
||||
* @param h_errnop pointer to an int where to store errors (instead of modifying
|
||||
* the global h_errno)
|
||||
* @return 0 on success, non-zero on error, additional error information
|
||||
* is stored in *h_errnop instead of h_errno to be thread-safe
|
||||
*/
|
||||
int
|
||||
lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
|
||||
size_t buflen, struct hostent **result, int *h_errnop)
|
||||
{
|
||||
err_t err;
|
||||
struct gethostbyname_r_helper *h;
|
||||
char *hostname;
|
||||
size_t namelen;
|
||||
int lh_errno;
|
||||
|
||||
if (h_errnop == NULL) {
|
||||
/* ensure h_errnop is never NULL */
|
||||
h_errnop = &lh_errno;
|
||||
}
|
||||
|
||||
if (result == NULL) {
|
||||
/* not all arguments given */
|
||||
*h_errnop = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* first thing to do: set *result to nothing */
|
||||
*result = NULL;
|
||||
if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
|
||||
/* not all arguments given */
|
||||
*h_errnop = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
namelen = strlen(name);
|
||||
if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) {
|
||||
/* buf can't hold the data needed + a copy of name */
|
||||
*h_errnop = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf);
|
||||
hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper);
|
||||
|
||||
/* query host IP address */
|
||||
err = netconn_gethostbyname(name, &h->addr);
|
||||
if (err != ERR_OK) {
|
||||
LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
|
||||
*h_errnop = HOST_NOT_FOUND;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy the hostname into buf */
|
||||
MEMCPY(hostname, name, namelen);
|
||||
hostname[namelen] = 0;
|
||||
|
||||
/* fill hostent */
|
||||
h->addr_list[0] = &h->addr;
|
||||
h->addr_list[1] = NULL;
|
||||
h->aliases = NULL;
|
||||
ret->h_name = hostname;
|
||||
ret->h_aliases = &h->aliases;
|
||||
ret->h_addrtype = AF_INET;
|
||||
ret->h_length = sizeof(ip_addr_t);
|
||||
ret->h_addr_list = (char**)&h->addr_list;
|
||||
|
||||
/* set result != NULL */
|
||||
*result = ret;
|
||||
|
||||
/* return success */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees one or more addrinfo structures returned by getaddrinfo(), along with
|
||||
* any additional storage associated with those structures. If the ai_next field
|
||||
* of the structure is not null, the entire list of structures is freed.
|
||||
*
|
||||
* @param ai struct addrinfo to free
|
||||
*/
|
||||
void
|
||||
lwip_freeaddrinfo(struct addrinfo *ai)
|
||||
{
|
||||
struct addrinfo *next;
|
||||
|
||||
while (ai != NULL) {
|
||||
next = ai->ai_next;
|
||||
memp_free(MEMP_NETDB, ai);
|
||||
ai = next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the name of a service location (for example, a host name) and/or
|
||||
* a service name and returns a set of socket addresses and associated
|
||||
* information to be used in creating a socket with which to address the
|
||||
* specified service.
|
||||
* Memory for the result is allocated internally and must be freed by calling
|
||||
* lwip_freeaddrinfo()!
|
||||
*
|
||||
* Due to a limitation in dns_gethostbyname, only the first address of a
|
||||
* host is returned.
|
||||
* Also, service names are not supported (only port numbers)!
|
||||
*
|
||||
* @param nodename descriptive name or address string of the host
|
||||
* (may be NULL -> local address)
|
||||
* @param servname port number as string of NULL
|
||||
* @param hints structure containing input values that set socktype and protocol
|
||||
* @param res pointer to a pointer where to store the result (set to NULL on failure)
|
||||
* @return 0 on success, non-zero on failure
|
||||
*
|
||||
* @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
|
||||
*/
|
||||
int
|
||||
lwip_getaddrinfo(const char *nodename, const char *servname,
|
||||
const struct addrinfo *hints, struct addrinfo **res)
|
||||
{
|
||||
err_t err;
|
||||
ip_addr_t addr;
|
||||
struct addrinfo *ai;
|
||||
struct sockaddr_storage *sa = NULL;
|
||||
int port_nr = 0;
|
||||
size_t total_size;
|
||||
size_t namelen = 0;
|
||||
int ai_family;
|
||||
|
||||
if (res == NULL) {
|
||||
return EAI_FAIL;
|
||||
}
|
||||
*res = NULL;
|
||||
if ((nodename == NULL) && (servname == NULL)) {
|
||||
return EAI_NONAME;
|
||||
}
|
||||
|
||||
if (hints != NULL) {
|
||||
ai_family = hints->ai_family;
|
||||
if ((ai_family != AF_UNSPEC)
|
||||
#if LWIP_IPV4
|
||||
&& (ai_family != AF_INET)
|
||||
#endif /* LWIP_IPV4 */
|
||||
#if LWIP_IPV6
|
||||
&& (ai_family != AF_INET6)
|
||||
#endif /* LWIP_IPV6 */
|
||||
) {
|
||||
return EAI_FAMILY;
|
||||
}
|
||||
} else {
|
||||
ai_family = AF_UNSPEC;
|
||||
}
|
||||
|
||||
if (servname != NULL) {
|
||||
/* service name specified: convert to port number
|
||||
* @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */
|
||||
port_nr = atoi(servname);
|
||||
if ((port_nr <= 0) || (port_nr > 0xffff)) {
|
||||
return EAI_SERVICE;
|
||||
}
|
||||
}
|
||||
|
||||
if (nodename != NULL) {
|
||||
/* service location specified, try to resolve */
|
||||
if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) {
|
||||
/* no DNS lookup, just parse for an address string */
|
||||
if (!ipaddr_aton(nodename, &addr)) {
|
||||
return EAI_NONAME;
|
||||
}
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
if ((IP_IS_V6_VAL(addr) && ai_family == AF_INET) ||
|
||||
(IP_IS_V4_VAL(addr) && ai_family == AF_INET6)) {
|
||||
return EAI_NONAME;
|
||||
}
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
} else {
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
/* AF_UNSPEC: prefer IPv4 */
|
||||
u8_t type = NETCONN_DNS_IPV4_IPV6;
|
||||
if (ai_family == AF_INET) {
|
||||
type = NETCONN_DNS_IPV4;
|
||||
} else if (ai_family == AF_INET6) {
|
||||
type = NETCONN_DNS_IPV6;
|
||||
}
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
err = netconn_gethostbyname_addrtype(nodename, &addr, type);
|
||||
if (err != ERR_OK) {
|
||||
return EAI_FAIL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* service location specified, use loopback address */
|
||||
if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
|
||||
ip_addr_set_any(ai_family == AF_INET6, &addr);
|
||||
} else {
|
||||
ip_addr_set_loopback(ai_family == AF_INET6, &addr);
|
||||
}
|
||||
}
|
||||
|
||||
total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
|
||||
if (nodename != NULL) {
|
||||
namelen = strlen(nodename);
|
||||
if (namelen > DNS_MAX_NAME_LENGTH) {
|
||||
/* invalid name length */
|
||||
return EAI_FAIL;
|
||||
}
|
||||
LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size);
|
||||
total_size += namelen + 1;
|
||||
}
|
||||
/* If this fails, please report to lwip-devel! :-) */
|
||||
LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
|
||||
total_size <= NETDB_ELEM_SIZE);
|
||||
ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
|
||||
if (ai == NULL) {
|
||||
return EAI_MEMORY;
|
||||
}
|
||||
memset(ai, 0, total_size);
|
||||
/* cast through void* to get rid of alignment warnings */
|
||||
sa = (struct sockaddr_storage *)(void*)((u8_t*)ai + sizeof(struct addrinfo));
|
||||
if (IP_IS_V6_VAL(addr)) {
|
||||
#if LWIP_IPV6
|
||||
struct sockaddr_in6 *sa6 = (struct sockaddr_in6*)sa;
|
||||
/* set up sockaddr */
|
||||
inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr));
|
||||
sa6->sin6_family = AF_INET6;
|
||||
sa6->sin6_len = sizeof(struct sockaddr_in6);
|
||||
sa6->sin6_port = lwip_htons((u16_t)port_nr);
|
||||
ai->ai_family = AF_INET6;
|
||||
#endif /* LWIP_IPV6 */
|
||||
} else {
|
||||
#if LWIP_IPV4
|
||||
struct sockaddr_in *sa4 = (struct sockaddr_in*)sa;
|
||||
/* set up sockaddr */
|
||||
inet_addr_from_ipaddr(&sa4->sin_addr, ip_2_ip4(&addr));
|
||||
sa4->sin_family = AF_INET;
|
||||
sa4->sin_len = sizeof(struct sockaddr_in);
|
||||
sa4->sin_port = lwip_htons((u16_t)port_nr);
|
||||
ai->ai_family = AF_INET;
|
||||
#endif /* LWIP_IPV4 */
|
||||
}
|
||||
|
||||
/* set up addrinfo */
|
||||
if (hints != NULL) {
|
||||
/* copy socktype & protocol from hints if specified */
|
||||
ai->ai_socktype = hints->ai_socktype;
|
||||
ai->ai_protocol = hints->ai_protocol;
|
||||
}
|
||||
if (nodename != NULL) {
|
||||
/* copy nodename to canonname if specified */
|
||||
ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
|
||||
MEMCPY(ai->ai_canonname, nodename, namelen);
|
||||
ai->ai_canonname[namelen] = 0;
|
||||
}
|
||||
ai->ai_addrlen = sizeof(struct sockaddr_storage);
|
||||
ai->ai_addr = (struct sockaddr*)sa;
|
||||
|
||||
*res = ai;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* LWIP_DNS && LWIP_SOCKET */
|
||||
221
src/api/netifapi.c
Normal file
221
src/api/netifapi.c
Normal file
@@ -0,0 +1,221 @@
|
||||
/**
|
||||
* @file
|
||||
* Network Interface Sequential API module
|
||||
*
|
||||
* @defgroup netifapi NETIF API
|
||||
* @ingroup sequential_api
|
||||
* Thread-safe functions to be called from non-TCPIP threads
|
||||
*
|
||||
* @defgroup netifapi_netif NETIF related
|
||||
* @ingroup netifapi
|
||||
* To be called from non-TCPIP threads
|
||||
*/
|
||||
|
||||
/*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/netifapi.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
|
||||
#define NETIFAPI_VAR_REF(name) API_VAR_REF(name)
|
||||
#define NETIFAPI_VAR_DECLARE(name) API_VAR_DECLARE(struct netifapi_msg, name)
|
||||
#define NETIFAPI_VAR_ALLOC(name) API_VAR_ALLOC(struct netifapi_msg, MEMP_NETIFAPI_MSG, name, ERR_MEM)
|
||||
#define NETIFAPI_VAR_FREE(name) API_VAR_FREE(MEMP_NETIFAPI_MSG, name)
|
||||
|
||||
/**
|
||||
* Call netif_add() inside the tcpip_thread context.
|
||||
*/
|
||||
static err_t
|
||||
netifapi_do_netif_add(struct tcpip_api_call_data *m)
|
||||
{
|
||||
/* cast through void* to silence alignment warnings.
|
||||
* We know it works because the structs have been instantiated as struct netifapi_msg */
|
||||
struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
|
||||
|
||||
if (!netif_add( msg->netif,
|
||||
#if LWIP_IPV4
|
||||
API_EXPR_REF(msg->msg.add.ipaddr),
|
||||
API_EXPR_REF(msg->msg.add.netmask),
|
||||
API_EXPR_REF(msg->msg.add.gw),
|
||||
#endif /* LWIP_IPV4 */
|
||||
msg->msg.add.state,
|
||||
msg->msg.add.init,
|
||||
msg->msg.add.input)) {
|
||||
return ERR_IF;
|
||||
} else {
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
/**
|
||||
* Call netif_set_addr() inside the tcpip_thread context.
|
||||
*/
|
||||
static err_t
|
||||
netifapi_do_netif_set_addr(struct tcpip_api_call_data *m)
|
||||
{
|
||||
/* cast through void* to silence alignment warnings.
|
||||
* We know it works because the structs have been instantiated as struct netifapi_msg */
|
||||
struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
|
||||
|
||||
netif_set_addr( msg->netif,
|
||||
API_EXPR_REF(msg->msg.add.ipaddr),
|
||||
API_EXPR_REF(msg->msg.add.netmask),
|
||||
API_EXPR_REF(msg->msg.add.gw));
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
/**
|
||||
* Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
|
||||
* tcpip_thread context.
|
||||
*/
|
||||
static err_t
|
||||
netifapi_do_netif_common(struct tcpip_api_call_data *m)
|
||||
{
|
||||
/* cast through void* to silence alignment warnings.
|
||||
* We know it works because the structs have been instantiated as struct netifapi_msg */
|
||||
struct netifapi_msg *msg = (struct netifapi_msg*)(void*)m;
|
||||
|
||||
if (msg->msg.common.errtfunc != NULL) {
|
||||
return msg->msg.common.errtfunc(msg->netif);
|
||||
} else {
|
||||
msg->msg.common.voidfunc(msg->netif);
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netifapi_netif
|
||||
* Call netif_add() in a thread-safe way by running that function inside the
|
||||
* tcpip_thread context.
|
||||
*
|
||||
* @note for params @see netif_add()
|
||||
*/
|
||||
err_t
|
||||
netifapi_netif_add(struct netif *netif,
|
||||
#if LWIP_IPV4
|
||||
const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
|
||||
#endif /* LWIP_IPV4 */
|
||||
void *state, netif_init_fn init, netif_input_fn input)
|
||||
{
|
||||
err_t err;
|
||||
NETIFAPI_VAR_DECLARE(msg);
|
||||
NETIFAPI_VAR_ALLOC(msg);
|
||||
|
||||
#if LWIP_IPV4
|
||||
if (ipaddr == NULL) {
|
||||
ipaddr = IP4_ADDR_ANY4;
|
||||
}
|
||||
if (netmask == NULL) {
|
||||
netmask = IP4_ADDR_ANY4;
|
||||
}
|
||||
if (gw == NULL) {
|
||||
gw = IP4_ADDR_ANY4;
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
NETIFAPI_VAR_REF(msg).netif = netif;
|
||||
#if LWIP_IPV4
|
||||
NETIFAPI_VAR_REF(msg).msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr);
|
||||
NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
|
||||
NETIFAPI_VAR_REF(msg).msg.add.gw = NETIFAPI_VAR_REF(gw);
|
||||
#endif /* LWIP_IPV4 */
|
||||
NETIFAPI_VAR_REF(msg).msg.add.state = state;
|
||||
NETIFAPI_VAR_REF(msg).msg.add.init = init;
|
||||
NETIFAPI_VAR_REF(msg).msg.add.input = input;
|
||||
err = tcpip_api_call(netifapi_do_netif_add, &API_VAR_REF(msg).call);
|
||||
NETIFAPI_VAR_FREE(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
/**
|
||||
* @ingroup netifapi_netif
|
||||
* Call netif_set_addr() in a thread-safe way by running that function inside the
|
||||
* tcpip_thread context.
|
||||
*
|
||||
* @note for params @see netif_set_addr()
|
||||
*/
|
||||
err_t
|
||||
netifapi_netif_set_addr(struct netif *netif,
|
||||
const ip4_addr_t *ipaddr,
|
||||
const ip4_addr_t *netmask,
|
||||
const ip4_addr_t *gw)
|
||||
{
|
||||
err_t err;
|
||||
NETIFAPI_VAR_DECLARE(msg);
|
||||
NETIFAPI_VAR_ALLOC(msg);
|
||||
|
||||
if (ipaddr == NULL) {
|
||||
ipaddr = IP4_ADDR_ANY4;
|
||||
}
|
||||
if (netmask == NULL) {
|
||||
netmask = IP4_ADDR_ANY4;
|
||||
}
|
||||
if (gw == NULL) {
|
||||
gw = IP4_ADDR_ANY4;
|
||||
}
|
||||
|
||||
NETIFAPI_VAR_REF(msg).netif = netif;
|
||||
NETIFAPI_VAR_REF(msg).msg.add.ipaddr = NETIFAPI_VAR_REF(ipaddr);
|
||||
NETIFAPI_VAR_REF(msg).msg.add.netmask = NETIFAPI_VAR_REF(netmask);
|
||||
NETIFAPI_VAR_REF(msg).msg.add.gw = NETIFAPI_VAR_REF(gw);
|
||||
err = tcpip_api_call(netifapi_do_netif_set_addr, &API_VAR_REF(msg).call);
|
||||
NETIFAPI_VAR_FREE(msg);
|
||||
return err;
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
/**
|
||||
* call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
|
||||
* way by running that function inside the tcpip_thread context.
|
||||
*
|
||||
* @note use only for functions where there is only "netif" parameter.
|
||||
*/
|
||||
err_t
|
||||
netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
|
||||
netifapi_errt_fn errtfunc)
|
||||
{
|
||||
err_t err;
|
||||
NETIFAPI_VAR_DECLARE(msg);
|
||||
NETIFAPI_VAR_ALLOC(msg);
|
||||
|
||||
NETIFAPI_VAR_REF(msg).netif = netif;
|
||||
NETIFAPI_VAR_REF(msg).msg.common.voidfunc = voidfunc;
|
||||
NETIFAPI_VAR_REF(msg).msg.common.errtfunc = errtfunc;
|
||||
err = tcpip_api_call(netifapi_do_netif_common, &API_VAR_REF(msg).call);
|
||||
NETIFAPI_VAR_FREE(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* LWIP_NETIF_API */
|
||||
3072
src/api/sockets.c
3072
src/api/sockets.c
File diff suppressed because it is too large
Load Diff
541
src/api/tcpip.c
541
src/api/tcpip.c
@@ -1,8 +1,14 @@
|
||||
/**
|
||||
* @file
|
||||
* Sequential API Main thread module
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
@@ -11,167 +17,502 @@
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/pbuf.h"
|
||||
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/init.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/etharp.h"
|
||||
#include "netif/ethernet.h"
|
||||
|
||||
#include "lwip/tcpip.h"
|
||||
#define TCPIP_MSG_VAR_REF(name) API_VAR_REF(name)
|
||||
#define TCPIP_MSG_VAR_DECLARE(name) API_VAR_DECLARE(struct tcpip_msg, name)
|
||||
#define TCPIP_MSG_VAR_ALLOC(name) API_VAR_ALLOC(struct tcpip_msg, MEMP_TCPIP_MSG_API, name, ERR_MEM)
|
||||
#define TCPIP_MSG_VAR_FREE(name) API_VAR_FREE(MEMP_TCPIP_MSG_API, name)
|
||||
|
||||
static void (* tcpip_init_done)(void *arg) = NULL;
|
||||
/* global variables */
|
||||
static tcpip_init_done_fn tcpip_init_done;
|
||||
static void *tcpip_init_done_arg;
|
||||
static sys_mbox_t mbox;
|
||||
#if LWIP_TCP
|
||||
static int tcpip_tcp_timer_active = 0;
|
||||
|
||||
#if LWIP_TCPIP_CORE_LOCKING
|
||||
/** The global semaphore to lock the stack. */
|
||||
sys_mutex_t lock_tcpip_core;
|
||||
#endif /* LWIP_TCPIP_CORE_LOCKING */
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
static void
|
||||
tcpip_tcp_timer(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
#if LWIP_TIMERS
|
||||
/* wait for a message, timeouts are processed while waiting */
|
||||
#define TCPIP_MBOX_FETCH(mbox, msg) sys_timeouts_mbox_fetch(mbox, msg)
|
||||
#else /* LWIP_TIMERS */
|
||||
/* wait for a message with timers disabled (e.g. pass a timer-check trigger into tcpip_thread) */
|
||||
#define TCPIP_MBOX_FETCH(mbox, msg) sys_mbox_fetch(mbox, msg)
|
||||
#endif /* LWIP_TIMERS */
|
||||
|
||||
tcp_tmr();
|
||||
if (tcp_active_pcbs || tcp_tw_pcbs) {
|
||||
sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
|
||||
} else {
|
||||
tcpip_tcp_timer_active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tcp_timer_needed(void)
|
||||
{
|
||||
if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
|
||||
tcpip_tcp_timer_active = 1;
|
||||
sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
|
||||
}
|
||||
}
|
||||
#endif /* LWIP_TCP */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/**
|
||||
* The main lwIP thread. This thread has exclusive access to lwIP core functions
|
||||
* (unless access to them is not locked). Other threads communicate with this
|
||||
* thread using message boxes.
|
||||
*
|
||||
* It also starts all the timers to make sure they are running in the right
|
||||
* thread context.
|
||||
*
|
||||
* @param arg unused argument
|
||||
*/
|
||||
static void
|
||||
tcpip_thread(void *arg)
|
||||
{
|
||||
struct tcpip_msg *msg;
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
|
||||
(void)arg;
|
||||
|
||||
ip_init();
|
||||
#if LWIP_UDP
|
||||
udp_init();
|
||||
#endif
|
||||
#if LWIP_TCP
|
||||
tcp_init();
|
||||
#endif
|
||||
if (tcpip_init_done != NULL) {
|
||||
tcpip_init_done(tcpip_init_done_arg);
|
||||
}
|
||||
|
||||
LOCK_TCPIP_CORE();
|
||||
while (1) { /* MAIN Loop */
|
||||
sys_mbox_fetch(mbox, (void *)&msg);
|
||||
UNLOCK_TCPIP_CORE();
|
||||
LWIP_TCPIP_THREAD_ALIVE();
|
||||
/* wait for a message, timeouts are processed while waiting */
|
||||
TCPIP_MBOX_FETCH(&mbox, (void **)&msg);
|
||||
LOCK_TCPIP_CORE();
|
||||
if (msg == NULL) {
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
|
||||
LWIP_ASSERT("tcpip_thread: invalid message", 0);
|
||||
continue;
|
||||
}
|
||||
switch (msg->type) {
|
||||
#if !LWIP_TCPIP_CORE_LOCKING
|
||||
case TCPIP_MSG_API:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
|
||||
api_msg_input(msg->msg.apimsg);
|
||||
msg->msg.api_msg.function(msg->msg.api_msg.msg);
|
||||
break;
|
||||
case TCPIP_MSG_INPUT:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: IP packet %p\n", (void *)msg));
|
||||
ip_input(msg->msg.inp.p, msg->msg.inp.netif);
|
||||
case TCPIP_MSG_API_CALL:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API CALL message %p\n", (void *)msg));
|
||||
msg->msg.api_call.arg->err = msg->msg.api_call.function(msg->msg.api_call.arg);
|
||||
sys_sem_signal(msg->msg.api_call.sem);
|
||||
break;
|
||||
#endif /* !LWIP_TCPIP_CORE_LOCKING */
|
||||
|
||||
#if !LWIP_TCPIP_CORE_LOCKING_INPUT
|
||||
case TCPIP_MSG_INPKT:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
|
||||
msg->msg.inp.input_fn(msg->msg.inp.p, msg->msg.inp.netif);
|
||||
memp_free(MEMP_TCPIP_MSG_INPKT, msg);
|
||||
break;
|
||||
#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
|
||||
|
||||
#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
|
||||
case TCPIP_MSG_TIMEOUT:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
|
||||
sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
|
||||
memp_free(MEMP_TCPIP_MSG_API, msg);
|
||||
break;
|
||||
case TCPIP_MSG_UNTIMEOUT:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
|
||||
sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
|
||||
memp_free(MEMP_TCPIP_MSG_API, msg);
|
||||
break;
|
||||
#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
|
||||
|
||||
case TCPIP_MSG_CALLBACK:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
|
||||
msg->msg.cb.f(msg->msg.cb.ctx);
|
||||
msg->msg.cb.function(msg->msg.cb.ctx);
|
||||
memp_free(MEMP_TCPIP_MSG_API, msg);
|
||||
break;
|
||||
|
||||
case TCPIP_MSG_CALLBACK_STATIC:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
|
||||
msg->msg.cb.function(msg->msg.cb.ctx);
|
||||
break;
|
||||
|
||||
default:
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
|
||||
LWIP_ASSERT("tcpip_thread: invalid message", 0);
|
||||
break;
|
||||
}
|
||||
memp_freep(MEMP_TCPIP_MSG, msg);
|
||||
}
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Pass a received packet to tcpip_thread for input processing
|
||||
*
|
||||
* @param p the received packet
|
||||
* @param inp the network interface on which the packet was received
|
||||
* @param input_fn input function to call
|
||||
*/
|
||||
err_t
|
||||
tcpip_inpkt(struct pbuf *p, struct netif *inp, netif_input_fn input_fn)
|
||||
{
|
||||
#if LWIP_TCPIP_CORE_LOCKING_INPUT
|
||||
err_t ret;
|
||||
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
|
||||
LOCK_TCPIP_CORE();
|
||||
ret = input_fn(p, inp);
|
||||
UNLOCK_TCPIP_CORE();
|
||||
return ret;
|
||||
#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
|
||||
struct tcpip_msg *msg;
|
||||
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
|
||||
if (msg == NULL) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
msg->type = TCPIP_MSG_INPKT;
|
||||
msg->msg.inp.p = p;
|
||||
msg->msg.inp.netif = inp;
|
||||
msg->msg.inp.input_fn = input_fn;
|
||||
if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
|
||||
memp_free(MEMP_TCPIP_MSG_INPKT, msg);
|
||||
return ERR_MEM;
|
||||
}
|
||||
return ERR_OK;
|
||||
#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup lwip_os
|
||||
* Pass a received packet to tcpip_thread for input processing with
|
||||
* ethernet_input or ip_input. Don't call directly, pass to netif_add()
|
||||
* and call netif->input().
|
||||
*
|
||||
* @param p the received packet, p->payload pointing to the Ethernet header or
|
||||
* to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
|
||||
* NETIF_FLAG_ETHERNET flags)
|
||||
* @param inp the network interface on which the packet was received
|
||||
*/
|
||||
err_t
|
||||
tcpip_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
struct tcpip_msg *msg;
|
||||
|
||||
msg = memp_mallocp(MEMP_TCPIP_MSG);
|
||||
if (msg == NULL) {
|
||||
pbuf_free(p);
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
msg->type = TCPIP_MSG_INPUT;
|
||||
msg->msg.inp.p = p;
|
||||
msg->msg.inp.netif = inp;
|
||||
sys_mbox_post(mbox, msg);
|
||||
return ERR_OK;
|
||||
#if LWIP_ETHERNET
|
||||
if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
|
||||
return tcpip_inpkt(p, inp, ethernet_input);
|
||||
} else
|
||||
#endif /* LWIP_ETHERNET */
|
||||
return tcpip_inpkt(p, inp, ip_input);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Call a specific function in the thread context of
|
||||
* tcpip_thread for easy access synchronization.
|
||||
* A function called in that way may access lwIP core code
|
||||
* without fearing concurrent access.
|
||||
*
|
||||
* @param function the function to call
|
||||
* @param ctx parameter passed to f
|
||||
* @param block 1 to block until the request is posted, 0 to non-blocking mode
|
||||
* @return ERR_OK if the function was called, another err_t if not
|
||||
*/
|
||||
err_t
|
||||
tcpip_callback(void (*f)(void *ctx), void *ctx)
|
||||
tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
|
||||
{
|
||||
struct tcpip_msg *msg;
|
||||
|
||||
msg = memp_mallocp(MEMP_TCPIP_MSG);
|
||||
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
|
||||
if (msg == NULL) {
|
||||
return ERR_MEM;
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
|
||||
msg->type = TCPIP_MSG_CALLBACK;
|
||||
msg->msg.cb.f = f;
|
||||
msg->msg.cb.function = function;
|
||||
msg->msg.cb.ctx = ctx;
|
||||
sys_mbox_post(mbox, msg);
|
||||
if (block) {
|
||||
sys_mbox_post(&mbox, msg);
|
||||
} else {
|
||||
if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
|
||||
memp_free(MEMP_TCPIP_MSG_API, msg);
|
||||
return ERR_MEM;
|
||||
}
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void
|
||||
tcpip_apimsg(struct api_msg *apimsg)
|
||||
|
||||
#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
|
||||
/**
|
||||
* call sys_timeout in tcpip_thread
|
||||
*
|
||||
* @param msecs time in milliseconds for timeout
|
||||
* @param h function to be called on timeout
|
||||
* @param arg argument to pass to timeout function h
|
||||
* @return ERR_MEM on memory error, ERR_OK otherwise
|
||||
*/
|
||||
err_t
|
||||
tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
|
||||
{
|
||||
struct tcpip_msg *msg;
|
||||
msg = memp_mallocp(MEMP_TCPIP_MSG);
|
||||
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
|
||||
if (msg == NULL) {
|
||||
memp_free(MEMP_API_MSG, apimsg);
|
||||
return;
|
||||
return ERR_MEM;
|
||||
}
|
||||
msg->type = TCPIP_MSG_API;
|
||||
msg->msg.apimsg = apimsg;
|
||||
sys_mbox_post(mbox, msg);
|
||||
|
||||
msg->type = TCPIP_MSG_TIMEOUT;
|
||||
msg->msg.tmo.msecs = msecs;
|
||||
msg->msg.tmo.h = h;
|
||||
msg->msg.tmo.arg = arg;
|
||||
sys_mbox_post(&mbox, msg);
|
||||
return ERR_OK;
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void
|
||||
tcpip_init(void (* initfunc)(void *), void *arg)
|
||||
|
||||
/**
|
||||
* call sys_untimeout in tcpip_thread
|
||||
*
|
||||
* @param h function to be called on timeout
|
||||
* @param arg argument to pass to timeout function h
|
||||
* @return ERR_MEM on memory error, ERR_OK otherwise
|
||||
*/
|
||||
err_t
|
||||
tcpip_untimeout(sys_timeout_handler h, void *arg)
|
||||
{
|
||||
struct tcpip_msg *msg;
|
||||
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
|
||||
if (msg == NULL) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
msg->type = TCPIP_MSG_UNTIMEOUT;
|
||||
msg->msg.tmo.h = h;
|
||||
msg->msg.tmo.arg = arg;
|
||||
sys_mbox_post(&mbox, msg);
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
|
||||
|
||||
|
||||
/**
|
||||
* Sends a message to TCPIP thread to call a function. Caller thread blocks on
|
||||
* on a provided semaphore, which ist NOT automatically signalled by TCPIP thread,
|
||||
* this has to be done by the user.
|
||||
* It is recommended to use LWIP_TCPIP_CORE_LOCKING since this is the way
|
||||
* with least runtime overhead.
|
||||
*
|
||||
* @param fn function to be called from TCPIP thread
|
||||
* @param apimsg argument to API function
|
||||
* @param sem semaphore to wait on
|
||||
* @return ERR_OK if the function was called, another err_t if not
|
||||
*/
|
||||
err_t
|
||||
tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t* sem)
|
||||
{
|
||||
#if LWIP_TCPIP_CORE_LOCKING
|
||||
LWIP_UNUSED_ARG(sem);
|
||||
LOCK_TCPIP_CORE();
|
||||
fn(apimsg);
|
||||
UNLOCK_TCPIP_CORE();
|
||||
return ERR_OK;
|
||||
#else /* LWIP_TCPIP_CORE_LOCKING */
|
||||
TCPIP_MSG_VAR_DECLARE(msg);
|
||||
|
||||
LWIP_ASSERT("semaphore not initialized", sys_sem_valid(sem));
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
TCPIP_MSG_VAR_ALLOC(msg);
|
||||
TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API;
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn;
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg;
|
||||
sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg));
|
||||
sys_arch_sem_wait(sem, 0);
|
||||
TCPIP_MSG_VAR_FREE(msg);
|
||||
return ERR_OK;
|
||||
#endif /* LWIP_TCPIP_CORE_LOCKING */
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously calls function in TCPIP thread and waits for its completion.
|
||||
* It is recommended to use LWIP_TCPIP_CORE_LOCKING (preferred) or
|
||||
* LWIP_NETCONN_SEM_PER_THREAD.
|
||||
* If not, a semaphore is created and destroyed on every call which is usually
|
||||
* an expensive/slow operation.
|
||||
* @param fn Function to call
|
||||
* @param call Call parameters
|
||||
* @return Return value from tcpip_api_call_fn
|
||||
*/
|
||||
err_t
|
||||
tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call)
|
||||
{
|
||||
#if LWIP_TCPIP_CORE_LOCKING
|
||||
err_t err;
|
||||
LOCK_TCPIP_CORE();
|
||||
err = fn(call);
|
||||
UNLOCK_TCPIP_CORE();
|
||||
return err;
|
||||
#else /* LWIP_TCPIP_CORE_LOCKING */
|
||||
TCPIP_MSG_VAR_DECLARE(msg);
|
||||
|
||||
#if !LWIP_NETCONN_SEM_PER_THREAD
|
||||
err_t err = sys_sem_new(&call->sem, 0);
|
||||
if (err != ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
|
||||
TCPIP_MSG_VAR_ALLOC(msg);
|
||||
TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API_CALL;
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_call.arg = call;
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_call.function = fn;
|
||||
#if LWIP_NETCONN_SEM_PER_THREAD
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = LWIP_NETCONN_THREAD_SEM_GET();
|
||||
#else /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
TCPIP_MSG_VAR_REF(msg).msg.api_call.sem = &call->sem;
|
||||
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
sys_mbox_post(&mbox, &TCPIP_MSG_VAR_REF(msg));
|
||||
sys_arch_sem_wait(TCPIP_MSG_VAR_REF(msg).msg.api_call.sem, 0);
|
||||
TCPIP_MSG_VAR_FREE(msg);
|
||||
|
||||
#if !LWIP_NETCONN_SEM_PER_THREAD
|
||||
sys_sem_free(&call->sem);
|
||||
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
|
||||
|
||||
return call->err;
|
||||
#endif /* LWIP_TCPIP_CORE_LOCKING */
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a structure for a static callback message and initialize it.
|
||||
* This is intended to be used to send "static" messages from interrupt context.
|
||||
*
|
||||
* @param function the function to call
|
||||
* @param ctx parameter passed to function
|
||||
* @return a struct pointer to pass to tcpip_trycallback().
|
||||
*/
|
||||
struct tcpip_callback_msg*
|
||||
tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
|
||||
{
|
||||
struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
|
||||
if (msg == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
msg->type = TCPIP_MSG_CALLBACK_STATIC;
|
||||
msg->msg.cb.function = function;
|
||||
msg->msg.cb.ctx = ctx;
|
||||
return (struct tcpip_callback_msg*)msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a callback message allocated by tcpip_callbackmsg_new().
|
||||
*
|
||||
* @param msg the message to free
|
||||
*/
|
||||
void
|
||||
tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg)
|
||||
{
|
||||
memp_free(MEMP_TCPIP_MSG_API, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to post a callback-message to the tcpip_thread mbox
|
||||
* This is intended to be used to send "static" messages from interrupt context.
|
||||
*
|
||||
* @param msg pointer to the message to post
|
||||
* @return sys_mbox_trypost() return code
|
||||
*/
|
||||
err_t
|
||||
tcpip_trycallback(struct tcpip_callback_msg* msg)
|
||||
{
|
||||
LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
|
||||
return sys_mbox_trypost(&mbox, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup lwip_os
|
||||
* Initialize this module:
|
||||
* - initialize all sub modules
|
||||
* - start the tcpip_thread
|
||||
*
|
||||
* @param initfunc a function to call when tcpip_thread is running and finished initializing
|
||||
* @param arg argument to pass to initfunc
|
||||
*/
|
||||
void
|
||||
tcpip_init(tcpip_init_done_fn initfunc, void *arg)
|
||||
{
|
||||
lwip_init();
|
||||
|
||||
tcpip_init_done = initfunc;
|
||||
tcpip_init_done_arg = arg;
|
||||
mbox = sys_mbox_new();
|
||||
sys_thread_new(tcpip_thread, NULL, TCPIP_THREAD_PRIO);
|
||||
if (sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
|
||||
LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
|
||||
}
|
||||
#if LWIP_TCPIP_CORE_LOCKING
|
||||
if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
|
||||
LWIP_ASSERT("failed to create lock_tcpip_core", 0);
|
||||
}
|
||||
#endif /* LWIP_TCPIP_CORE_LOCKING */
|
||||
|
||||
sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Simple callback function used with tcpip_callback to free a pbuf
|
||||
* (pbuf_free has a wrong signature for tcpip_callback)
|
||||
*
|
||||
* @param p The pbuf (chain) to be dereferenced.
|
||||
*/
|
||||
static void
|
||||
pbuf_free_int(void *p)
|
||||
{
|
||||
struct pbuf *q = (struct pbuf *)p;
|
||||
pbuf_free(q);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple wrapper function that allows you to free a pbuf from interrupt context.
|
||||
*
|
||||
* @param p The pbuf (chain) to be dereferenced.
|
||||
* @return ERR_OK if callback could be enqueued, an err_t if not
|
||||
*/
|
||||
err_t
|
||||
pbuf_free_callback(struct pbuf *p)
|
||||
{
|
||||
return tcpip_callback_with_block(pbuf_free_int, p, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple wrapper function that allows you to free heap memory from
|
||||
* interrupt context.
|
||||
*
|
||||
* @param m the heap memory to free
|
||||
* @return ERR_OK if callback could be enqueued, an err_t if not
|
||||
*/
|
||||
err_t
|
||||
mem_free_callback(void *m)
|
||||
{
|
||||
return tcpip_callback_with_block(mem_free, m, 0);
|
||||
}
|
||||
|
||||
#endif /* !NO_SYS */
|
||||
|
||||
179
src/apps/httpd/fs.c
Normal file
179
src/apps/httpd/fs.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/apps/httpd_opts.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/apps/fs.h"
|
||||
#include "fsdata.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#if HTTPD_USE_CUSTOM_FSDATA
|
||||
#include "fsdata_custom.c"
|
||||
#else /* HTTPD_USE_CUSTOM_FSDATA */
|
||||
#include "fsdata.c"
|
||||
#endif /* HTTPD_USE_CUSTOM_FSDATA */
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
#if LWIP_HTTPD_CUSTOM_FILES
|
||||
int fs_open_custom(struct fs_file *file, const char *name);
|
||||
void fs_close_custom(struct fs_file *file);
|
||||
#if LWIP_HTTPD_FS_ASYNC_READ
|
||||
u8_t fs_canread_custom(struct fs_file *file);
|
||||
u8_t fs_wait_read_custom(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg);
|
||||
int fs_read_async_custom(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg);
|
||||
#else /* LWIP_HTTPD_FS_ASYNC_READ */
|
||||
int fs_read_custom(struct fs_file *file, char *buffer, int count);
|
||||
#endif /* LWIP_HTTPD_FS_ASYNC_READ */
|
||||
#endif /* LWIP_HTTPD_CUSTOM_FILES */
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
err_t
|
||||
fs_open(struct fs_file *file, const char *name)
|
||||
{
|
||||
const struct fsdata_file *f;
|
||||
|
||||
if ((file == NULL) || (name == NULL)) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
#if LWIP_HTTPD_CUSTOM_FILES
|
||||
if (fs_open_custom(file, name)) {
|
||||
file->is_custom_file = 1;
|
||||
return ERR_OK;
|
||||
}
|
||||
file->is_custom_file = 0;
|
||||
#endif /* LWIP_HTTPD_CUSTOM_FILES */
|
||||
|
||||
for (f = FS_ROOT; f != NULL; f = f->next) {
|
||||
if (!strcmp(name, (const char *)f->name)) {
|
||||
file->data = (const char *)f->data;
|
||||
file->len = f->len;
|
||||
file->index = f->len;
|
||||
file->pextension = NULL;
|
||||
file->flags = f->flags;
|
||||
#if HTTPD_PRECALCULATED_CHECKSUM
|
||||
file->chksum_count = f->chksum_count;
|
||||
file->chksum = f->chksum;
|
||||
#endif /* HTTPD_PRECALCULATED_CHECKSUM */
|
||||
#if LWIP_HTTPD_FILE_STATE
|
||||
file->state = fs_state_init(file, name);
|
||||
#endif /* #if LWIP_HTTPD_FILE_STATE */
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
/* file not found */
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void
|
||||
fs_close(struct fs_file *file)
|
||||
{
|
||||
#if LWIP_HTTPD_CUSTOM_FILES
|
||||
if (file->is_custom_file) {
|
||||
fs_close_custom(file);
|
||||
}
|
||||
#endif /* LWIP_HTTPD_CUSTOM_FILES */
|
||||
#if LWIP_HTTPD_FILE_STATE
|
||||
fs_state_free(file, file->state);
|
||||
#endif /* #if LWIP_HTTPD_FILE_STATE */
|
||||
LWIP_UNUSED_ARG(file);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
#if LWIP_HTTPD_DYNAMIC_FILE_READ
|
||||
#if LWIP_HTTPD_FS_ASYNC_READ
|
||||
int
|
||||
fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg)
|
||||
#else /* LWIP_HTTPD_FS_ASYNC_READ */
|
||||
int
|
||||
fs_read(struct fs_file *file, char *buffer, int count)
|
||||
#endif /* LWIP_HTTPD_FS_ASYNC_READ */
|
||||
{
|
||||
int read;
|
||||
if(file->index == file->len) {
|
||||
return FS_READ_EOF;
|
||||
}
|
||||
#if LWIP_HTTPD_FS_ASYNC_READ
|
||||
LWIP_UNUSED_ARG(callback_fn);
|
||||
LWIP_UNUSED_ARG(callback_arg);
|
||||
#endif /* LWIP_HTTPD_FS_ASYNC_READ */
|
||||
#if LWIP_HTTPD_CUSTOM_FILES
|
||||
if (file->is_custom_file) {
|
||||
#if LWIP_HTTPD_FS_ASYNC_READ
|
||||
return fs_read_async_custom(file, buffer, count, callback_fn, callback_arg);
|
||||
#else /* LWIP_HTTPD_FS_ASYNC_READ */
|
||||
return fs_read_custom(file, buffer, count);
|
||||
#endif /* LWIP_HTTPD_FS_ASYNC_READ */
|
||||
}
|
||||
#endif /* LWIP_HTTPD_CUSTOM_FILES */
|
||||
|
||||
read = file->len - file->index;
|
||||
if(read > count) {
|
||||
read = count;
|
||||
}
|
||||
|
||||
MEMCPY(buffer, (file->data + file->index), read);
|
||||
file->index += read;
|
||||
|
||||
return(read);
|
||||
}
|
||||
#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
#if LWIP_HTTPD_FS_ASYNC_READ
|
||||
int
|
||||
fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg)
|
||||
{
|
||||
if (file != NULL) {
|
||||
#if LWIP_HTTPD_FS_ASYNC_READ
|
||||
#if LWIP_HTTPD_CUSTOM_FILES
|
||||
if (!fs_canread_custom(file)) {
|
||||
if (fs_wait_read_custom(file, callback_fn, callback_arg)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#else /* LWIP_HTTPD_CUSTOM_FILES */
|
||||
LWIP_UNUSED_ARG(callback_fn);
|
||||
LWIP_UNUSED_ARG(callback_arg);
|
||||
#endif /* LWIP_HTTPD_CUSTOM_FILES */
|
||||
#endif /* LWIP_HTTPD_FS_ASYNC_READ */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif /* LWIP_HTTPD_FS_ASYNC_READ */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
int
|
||||
fs_bytes_left(struct fs_file *file)
|
||||
{
|
||||
return file->len - file->index;
|
||||
}
|
||||
21
src/apps/httpd/fs/404.html
Normal file
21
src/apps/httpd/fs/404.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<html>
|
||||
<head><title>lwIP - A Lightweight TCP/IP Stack</title></head>
|
||||
<body bgcolor="white" text="black">
|
||||
|
||||
<table width="100%">
|
||||
<tr valign="top"><td width="80">
|
||||
<a href="http://www.sics.se/"><img src="/img/sics.gif"
|
||||
border="0" alt="SICS logo" title="SICS logo"></a>
|
||||
</td><td width="500">
|
||||
<h1>lwIP - A Lightweight TCP/IP Stack</h1>
|
||||
<h2>404 - Page not found</h2>
|
||||
<p>
|
||||
Sorry, the page you are requesting was not found on this
|
||||
server.
|
||||
</p>
|
||||
</td><td>
|
||||
|
||||
</td></tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
BIN
src/apps/httpd/fs/img/sics.gif
Normal file
BIN
src/apps/httpd/fs/img/sics.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 724 B |
47
src/apps/httpd/fs/index.html
Normal file
47
src/apps/httpd/fs/index.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<html>
|
||||
<head><title>lwIP - A Lightweight TCP/IP Stack</title></head>
|
||||
<body bgcolor="white" text="black">
|
||||
|
||||
<table width="100%">
|
||||
<tr valign="top"><td width="80">
|
||||
<a href="http://www.sics.se/"><img src="/img/sics.gif"
|
||||
border="0" alt="SICS logo" title="SICS logo"></a>
|
||||
</td><td width="500">
|
||||
<h1>lwIP - A Lightweight TCP/IP Stack</h1>
|
||||
<p>
|
||||
The web page you are watching was served by a simple web
|
||||
server running on top of the lightweight TCP/IP stack <a
|
||||
href="http://www.sics.se/~adam/lwip/">lwIP</a>.
|
||||
</p>
|
||||
<p>
|
||||
lwIP is an open source implementation of the TCP/IP
|
||||
protocol suite that was originally written by <a
|
||||
href="http://www.sics.se/~adam/lwip/">Adam Dunkels
|
||||
of the Swedish Institute of Computer Science</a> but now is
|
||||
being actively developed by a team of developers
|
||||
distributed world-wide. Since it's release, lwIP has
|
||||
spurred a lot of interest and has been ported to several
|
||||
platforms and operating systems. lwIP can be used either
|
||||
with or without an underlying OS.
|
||||
</p>
|
||||
<p>
|
||||
The focus of the lwIP TCP/IP implementation is to reduce
|
||||
the RAM usage while still having a full scale TCP. This
|
||||
makes lwIP suitable for use in embedded systems with tens
|
||||
of kilobytes of free RAM and room for around 40 kilobytes
|
||||
of code ROM.
|
||||
</p>
|
||||
<p>
|
||||
More information about lwIP can be found at the lwIP
|
||||
homepage at <a
|
||||
href="http://savannah.nongnu.org/projects/lwip/">http://savannah.nongnu.org/projects/lwip/</a>
|
||||
or at the lwIP wiki at <a
|
||||
href="http://lwip.wikia.com/">http://lwip.wikia.com/</a>.
|
||||
</p>
|
||||
</td><td>
|
||||
|
||||
</td></tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
298
src/apps/httpd/fsdata.c
Normal file
298
src/apps/httpd/fsdata.c
Normal file
@@ -0,0 +1,298 @@
|
||||
#include "lwip/apps/fs.h"
|
||||
#include "lwip/def.h"
|
||||
#include "fsdata.h"
|
||||
|
||||
|
||||
#define file_NULL (struct fsdata_file *) NULL
|
||||
|
||||
|
||||
static const unsigned int dummy_align__img_sics_gif = 0;
|
||||
static const unsigned char data__img_sics_gif[] = {
|
||||
/* /img/sics.gif (14 chars) */
|
||||
0x2f,0x69,0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x00,0x00,0x00,
|
||||
|
||||
/* HTTP header */
|
||||
/* "HTTP/1.0 200 OK
|
||||
" (17 bytes) */
|
||||
0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d,
|
||||
0x0a,
|
||||
/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)
|
||||
" (63 bytes) */
|
||||
0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33,
|
||||
0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,
|
||||
0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,
|
||||
0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
|
||||
/* "Content-type: image/gif
|
||||
|
||||
" (27 bytes) */
|
||||
0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x69,0x6d,
|
||||
0x61,0x67,0x65,0x2f,0x67,0x69,0x66,0x0d,0x0a,0x0d,0x0a,
|
||||
/* raw file data (724 bytes) */
|
||||
0x47,0x49,0x46,0x38,0x39,0x61,0x46,0x00,0x22,0x00,0xa5,0x00,0x00,0xd9,0x2b,0x39,
|
||||
0x6a,0x6a,0x6a,0xbf,0xbf,0xbf,0x93,0x93,0x93,0x0f,0x0f,0x0f,0xb0,0xb0,0xb0,0xa6,
|
||||
0xa6,0xa6,0x80,0x80,0x80,0x76,0x76,0x76,0x1e,0x1e,0x1e,0x9d,0x9d,0x9d,0x2e,0x2e,
|
||||
0x2e,0x49,0x49,0x49,0x54,0x54,0x54,0x8a,0x8a,0x8a,0x60,0x60,0x60,0xc6,0xa6,0x99,
|
||||
0xbd,0xb5,0xb2,0xc2,0xab,0xa1,0xd9,0x41,0x40,0xd5,0x67,0x55,0xc0,0xb0,0xaa,0xd5,
|
||||
0x5e,0x4e,0xd6,0x50,0x45,0xcc,0x93,0x7d,0xc8,0xa1,0x90,0xce,0x8b,0x76,0xd2,0x7b,
|
||||
0x65,0xd1,0x84,0x6d,0xc9,0x99,0x86,0x3a,0x3a,0x3a,0x00,0x00,0x00,0xb8,0xb8,0xb8,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
|
||||
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x2c,0x00,0x00,
|
||||
0x00,0x00,0x46,0x00,0x22,0x00,0x00,0x06,0xfe,0x40,0x90,0x70,0x48,0x2c,0x1a,0x8f,
|
||||
0xc8,0xa4,0x72,0xc9,0x6c,0x3a,0x9f,0xd0,0xa8,0x74,0x4a,0xad,0x5a,0xaf,0xd8,0xac,
|
||||
0x76,0xa9,0x40,0x04,0xbe,0x83,0xe2,0x60,0x3c,0x50,0x20,0x0d,0x8e,0x6f,0x00,0x31,
|
||||
0x28,0x1c,0x0d,0x07,0xb5,0xc3,0x60,0x75,0x24,0x3e,0xf8,0xfc,0x87,0x11,0x06,0xe9,
|
||||
0x3d,0x46,0x07,0x0b,0x7a,0x7a,0x7c,0x43,0x06,0x1e,0x84,0x78,0x0b,0x07,0x6e,0x51,
|
||||
0x01,0x8a,0x84,0x08,0x7e,0x79,0x80,0x87,0x89,0x91,0x7a,0x93,0x0a,0x04,0x99,0x78,
|
||||
0x96,0x4f,0x03,0x9e,0x79,0x01,0x94,0x9f,0x43,0x9c,0xa3,0xa4,0x05,0x77,0xa3,0xa0,
|
||||
0x4e,0x98,0x79,0x0b,0x1e,0x83,0xa4,0xa6,0x1f,0x96,0x05,0x9d,0xaa,0x78,0x01,0x07,
|
||||
0x84,0x04,0x1e,0x1e,0xbb,0xb8,0x51,0x84,0x0e,0x43,0x05,0x07,0x77,0xa5,0x7f,0x42,
|
||||
0xb1,0xb2,0x01,0x63,0x08,0x0d,0xbb,0x01,0x0c,0x7a,0x0d,0x44,0x0e,0xd8,0xaf,0x4c,
|
||||
0x05,0x7a,0x04,0x47,0x07,0x07,0xb7,0x80,0xa2,0xe1,0x7d,0x44,0x05,0x01,0x04,0x01,
|
||||
0xd0,0xea,0x87,0x93,0x4f,0xe0,0x9a,0x49,0xce,0xd8,0x79,0x04,0x66,0x20,0x15,0x10,
|
||||
0x10,0x11,0x92,0x29,0x80,0xb6,0xc0,0x91,0x15,0x45,0x1e,0x90,0x19,0x71,0x46,0xa8,
|
||||
0x5c,0x04,0x0e,0x00,0x22,0x4e,0xe8,0x40,0x24,0x9f,0x3e,0x04,0x06,0xa7,0x58,0xd4,
|
||||
0x93,0xa0,0x1c,0x91,0x3f,0xe8,0xf0,0x88,0x03,0xb1,0x21,0xa2,0x49,0x00,0x19,0x86,
|
||||
0xfc,0x52,0x44,0xe0,0x01,0x9d,0x29,0x21,0x15,0x25,0x50,0xf7,0x67,0x25,0x1e,0x06,
|
||||
0xfd,0x4e,0x9a,0xb4,0x90,0xac,0x15,0xfa,0xcb,0x52,0x53,0x1e,0x8c,0xf2,0xf8,0x07,
|
||||
0x92,0x2d,0x08,0x3a,0x4d,0x12,0x49,0x95,0x49,0xdb,0x14,0x04,0xc4,0x14,0x85,0x29,
|
||||
0xaa,0xe7,0x01,0x08,0xa4,0x49,0x01,0x14,0x51,0xe0,0x53,0x91,0xd5,0x29,0x06,0x1a,
|
||||
0x64,0x02,0xf4,0xc7,0x81,0x9e,0x05,0x20,0x22,0x64,0xa5,0x30,0xae,0xab,0x9e,0x97,
|
||||
0x53,0xd8,0xb9,0xfd,0x50,0xef,0x93,0x02,0x42,0x74,0x34,0xe8,0x9c,0x20,0x21,0xc9,
|
||||
0x01,0x68,0x78,0xe6,0x55,0x29,0x20,0x56,0x4f,0x4c,0x40,0x51,0x71,0x82,0xc0,0x70,
|
||||
0x21,0x22,0x85,0xbe,0x4b,0x1c,0x44,0x05,0xea,0xa4,0x01,0xbf,0x22,0xb5,0xf0,0x1c,
|
||||
0x06,0x51,0x38,0x8f,0xe0,0x22,0xec,0x18,0xac,0x39,0x22,0xd4,0xd6,0x93,0x44,0x01,
|
||||
0x32,0x82,0xc8,0xfc,0x61,0xb3,0x01,0x45,0x0c,0x2e,0x83,0x30,0xd0,0x0e,0x17,0x24,
|
||||
0x0f,0x70,0x85,0x94,0xee,0x05,0x05,0x53,0x4b,0x32,0x1b,0x3f,0x98,0xd3,0x1d,0x29,
|
||||
0x81,0xb0,0xae,0x1e,0x8c,0x7e,0x68,0xe0,0x60,0x5a,0x54,0x8f,0xb0,0x78,0x69,0x73,
|
||||
0x06,0xa2,0x00,0x6b,0x57,0xca,0x3d,0x11,0x50,0xbd,0x04,0x30,0x4b,0x3a,0xd4,0xab,
|
||||
0x5f,0x1f,0x9b,0x3d,0x13,0x74,0x27,0x88,0x3c,0x25,0xe0,0x17,0xbe,0x7a,0x79,0x45,
|
||||
0x0d,0x0c,0xb0,0x8b,0xda,0x90,0xca,0x80,0x06,0x5d,0x17,0x60,0x1c,0x22,0x4c,0xd8,
|
||||
0x57,0x22,0x06,0x20,0x00,0x98,0x07,0x08,0xe4,0x56,0x80,0x80,0x1c,0xc5,0xb7,0xc5,
|
||||
0x82,0x0c,0x36,0xe8,0xe0,0x83,0x10,0x46,0x28,0xe1,0x84,0x14,0x56,0x68,0xa1,0x10,
|
||||
0x41,0x00,0x00,0x3b,};
|
||||
|
||||
static const unsigned int dummy_align__404_html = 1;
|
||||
static const unsigned char data__404_html[] = {
|
||||
/* /404.html (10 chars) */
|
||||
0x2f,0x34,0x30,0x34,0x2e,0x68,0x74,0x6d,0x6c,0x00,0x00,0x00,
|
||||
|
||||
/* HTTP header */
|
||||
/* "HTTP/1.0 404 File not found
|
||||
" (29 bytes) */
|
||||
0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x34,0x30,0x34,0x20,0x46,0x69,0x6c,
|
||||
0x65,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x0d,0x0a,
|
||||
/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)
|
||||
" (63 bytes) */
|
||||
0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33,
|
||||
0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,
|
||||
0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,
|
||||
0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
|
||||
/* "Content-type: text/html
|
||||
|
||||
" (27 bytes) */
|
||||
0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
|
||||
0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a,
|
||||
/* raw file data (565 bytes) */
|
||||
0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74,
|
||||
0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69,
|
||||
0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,
|
||||
0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f,
|
||||
0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63,
|
||||
0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78,
|
||||
0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20,
|
||||
0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,
|
||||
0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74,
|
||||
0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c,
|
||||
0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20,
|
||||
0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
|
||||
0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
|
||||
0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69,
|
||||
0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20,
|
||||
0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d,
|
||||
0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c,
|
||||
0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f,
|
||||
0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69,
|
||||
0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09,
|
||||
0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,
|
||||
0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,
|
||||
0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20,
|
||||
0x20,0x3c,0x68,0x32,0x3e,0x34,0x30,0x34,0x20,0x2d,0x20,0x50,0x61,0x67,0x65,0x20,
|
||||
0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x3c,0x2f,0x68,0x32,0x3e,0x0d,0x0a,
|
||||
0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x53,0x6f,0x72,
|
||||
0x72,0x79,0x2c,0x20,0x74,0x68,0x65,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75,
|
||||
0x20,0x61,0x72,0x65,0x20,0x72,0x65,0x71,0x75,0x65,0x73,0x74,0x69,0x6e,0x67,0x20,
|
||||
0x77,0x61,0x73,0x20,0x6e,0x6f,0x74,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x6f,0x6e,
|
||||
0x20,0x74,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76,
|
||||
0x65,0x72,0x2e,0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,
|
||||
0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e,
|
||||
0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72,
|
||||
0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65,
|
||||
0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74,
|
||||
0x6d,0x6c,0x3e,0x0d,0x0a,};
|
||||
|
||||
static const unsigned int dummy_align__index_html = 2;
|
||||
static const unsigned char data__index_html[] = {
|
||||
/* /index.html (12 chars) */
|
||||
0x2f,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,0x00,
|
||||
|
||||
/* HTTP header */
|
||||
/* "HTTP/1.0 200 OK
|
||||
" (17 bytes) */
|
||||
0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x30,0x20,0x32,0x30,0x30,0x20,0x4f,0x4b,0x0d,
|
||||
0x0a,
|
||||
/* "Server: lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)
|
||||
" (63 bytes) */
|
||||
0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x31,0x2e,0x33,
|
||||
0x2e,0x31,0x20,0x28,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,
|
||||
0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,
|
||||
0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x29,0x0d,0x0a,
|
||||
/* "Content-type: text/html
|
||||
|
||||
" (27 bytes) */
|
||||
0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x74,0x79,0x70,0x65,0x3a,0x20,0x74,0x65,
|
||||
0x78,0x74,0x2f,0x68,0x74,0x6d,0x6c,0x0d,0x0a,0x0d,0x0a,
|
||||
/* raw file data (1751 bytes) */
|
||||
0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x0d,0x0a,0x3c,0x68,0x65,0x61,0x64,0x3e,0x3c,0x74,
|
||||
0x69,0x74,0x6c,0x65,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,0x69,
|
||||
0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,
|
||||
0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x74,0x69,0x74,0x6c,0x65,0x3e,0x3c,0x2f,
|
||||
0x68,0x65,0x61,0x64,0x3e,0x0d,0x0a,0x3c,0x62,0x6f,0x64,0x79,0x20,0x62,0x67,0x63,
|
||||
0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x77,0x68,0x69,0x74,0x65,0x22,0x20,0x74,0x65,0x78,
|
||||
0x74,0x3d,0x22,0x62,0x6c,0x61,0x63,0x6b,0x22,0x3e,0x0d,0x0a,0x0d,0x0a,0x20,0x20,
|
||||
0x20,0x20,0x3c,0x74,0x61,0x62,0x6c,0x65,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,
|
||||
0x31,0x30,0x30,0x25,0x22,0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x74,
|
||||
0x72,0x20,0x76,0x61,0x6c,0x69,0x67,0x6e,0x3d,0x22,0x74,0x6f,0x70,0x22,0x3e,0x3c,
|
||||
0x74,0x64,0x20,0x77,0x69,0x64,0x74,0x68,0x3d,0x22,0x38,0x30,0x22,0x3e,0x09,0x20,
|
||||
0x20,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x61,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
|
||||
0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
|
||||
0x65,0x2f,0x22,0x3e,0x3c,0x69,0x6d,0x67,0x20,0x73,0x72,0x63,0x3d,0x22,0x2f,0x69,
|
||||
0x6d,0x67,0x2f,0x73,0x69,0x63,0x73,0x2e,0x67,0x69,0x66,0x22,0x0d,0x0a,0x09,0x20,
|
||||
0x20,0x62,0x6f,0x72,0x64,0x65,0x72,0x3d,0x22,0x30,0x22,0x20,0x61,0x6c,0x74,0x3d,
|
||||
0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x20,0x74,0x69,0x74,0x6c,
|
||||
0x65,0x3d,0x22,0x53,0x49,0x43,0x53,0x20,0x6c,0x6f,0x67,0x6f,0x22,0x3e,0x3c,0x2f,
|
||||
0x61,0x3e,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x20,0x77,0x69,
|
||||
0x64,0x74,0x68,0x3d,0x22,0x35,0x30,0x30,0x22,0x3e,0x09,0x20,0x20,0x0d,0x0a,0x09,
|
||||
0x20,0x20,0x3c,0x68,0x31,0x3e,0x6c,0x77,0x49,0x50,0x20,0x2d,0x20,0x41,0x20,0x4c,
|
||||
0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,
|
||||
0x50,0x20,0x53,0x74,0x61,0x63,0x6b,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x09,0x20,
|
||||
0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x77,
|
||||
0x65,0x62,0x20,0x70,0x61,0x67,0x65,0x20,0x79,0x6f,0x75,0x20,0x61,0x72,0x65,0x20,
|
||||
0x77,0x61,0x74,0x63,0x68,0x69,0x6e,0x67,0x20,0x77,0x61,0x73,0x20,0x73,0x65,0x72,
|
||||
0x76,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x73,0x69,0x6d,0x70,0x6c,0x65,0x20,
|
||||
0x77,0x65,0x62,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x65,0x72,0x76,0x65,0x72,
|
||||
0x20,0x72,0x75,0x6e,0x6e,0x69,0x6e,0x67,0x20,0x6f,0x6e,0x20,0x74,0x6f,0x70,0x20,
|
||||
0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x69,0x67,0x68,0x74,0x77,0x65,0x69,0x67,
|
||||
0x68,0x74,0x20,0x54,0x43,0x50,0x2f,0x49,0x50,0x20,0x73,0x74,0x61,0x63,0x6b,0x20,
|
||||
0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,
|
||||
0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,
|
||||
0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f,0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x6c,
|
||||
0x77,0x49,0x50,0x3c,0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,
|
||||
0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,
|
||||
0x6c,0x77,0x49,0x50,0x20,0x69,0x73,0x20,0x61,0x6e,0x20,0x6f,0x70,0x65,0x6e,0x20,
|
||||
0x73,0x6f,0x75,0x72,0x63,0x65,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,
|
||||
0x61,0x74,0x69,0x6f,0x6e,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x54,0x43,0x50,
|
||||
0x2f,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x72,0x6f,0x74,0x6f,0x63,
|
||||
0x6f,0x6c,0x20,0x73,0x75,0x69,0x74,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x77,0x61,
|
||||
0x73,0x20,0x6f,0x72,0x69,0x67,0x69,0x6e,0x61,0x6c,0x6c,0x79,0x20,0x77,0x72,0x69,
|
||||
0x74,0x74,0x65,0x6e,0x20,0x62,0x79,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,
|
||||
0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x77,0x77,
|
||||
0x77,0x2e,0x73,0x69,0x63,0x73,0x2e,0x73,0x65,0x2f,0x7e,0x61,0x64,0x61,0x6d,0x2f,
|
||||
0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x41,0x64,0x61,0x6d,0x20,0x44,0x75,0x6e,0x6b,
|
||||
0x65,0x6c,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,
|
||||
0x20,0x53,0x77,0x65,0x64,0x69,0x73,0x68,0x20,0x49,0x6e,0x73,0x74,0x69,0x74,0x75,
|
||||
0x74,0x65,0x20,0x6f,0x66,0x20,0x43,0x6f,0x6d,0x70,0x75,0x74,0x65,0x72,0x20,0x53,
|
||||
0x63,0x69,0x65,0x6e,0x63,0x65,0x3c,0x2f,0x61,0x3e,0x20,0x62,0x75,0x74,0x20,0x6e,
|
||||
0x6f,0x77,0x20,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x62,0x65,0x69,0x6e,
|
||||
0x67,0x20,0x61,0x63,0x74,0x69,0x76,0x65,0x6c,0x79,0x20,0x64,0x65,0x76,0x65,0x6c,
|
||||
0x6f,0x70,0x65,0x64,0x20,0x62,0x79,0x20,0x61,0x20,0x74,0x65,0x61,0x6d,0x20,0x6f,
|
||||
0x66,0x20,0x64,0x65,0x76,0x65,0x6c,0x6f,0x70,0x65,0x72,0x73,0x0d,0x0a,0x09,0x20,
|
||||
0x20,0x20,0x20,0x64,0x69,0x73,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x64,0x20,0x77,
|
||||
0x6f,0x72,0x6c,0x64,0x2d,0x77,0x69,0x64,0x65,0x2e,0x20,0x53,0x69,0x6e,0x63,0x65,
|
||||
0x20,0x69,0x74,0x27,0x73,0x20,0x72,0x65,0x6c,0x65,0x61,0x73,0x65,0x2c,0x20,0x6c,
|
||||
0x77,0x49,0x50,0x20,0x68,0x61,0x73,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x73,0x70,
|
||||
0x75,0x72,0x72,0x65,0x64,0x20,0x61,0x20,0x6c,0x6f,0x74,0x20,0x6f,0x66,0x20,0x69,
|
||||
0x6e,0x74,0x65,0x72,0x65,0x73,0x74,0x20,0x61,0x6e,0x64,0x20,0x68,0x61,0x73,0x20,
|
||||
0x62,0x65,0x65,0x6e,0x20,0x70,0x6f,0x72,0x74,0x65,0x64,0x20,0x74,0x6f,0x20,0x73,
|
||||
0x65,0x76,0x65,0x72,0x61,0x6c,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x70,0x6c,0x61,
|
||||
0x74,0x66,0x6f,0x72,0x6d,0x73,0x20,0x61,0x6e,0x64,0x20,0x6f,0x70,0x65,0x72,0x61,
|
||||
0x74,0x69,0x6e,0x67,0x20,0x73,0x79,0x73,0x74,0x65,0x6d,0x73,0x2e,0x20,0x6c,0x77,
|
||||
0x49,0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x65,
|
||||
0x69,0x74,0x68,0x65,0x72,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x77,0x69,0x74,0x68,
|
||||
0x20,0x6f,0x72,0x20,0x77,0x69,0x74,0x68,0x6f,0x75,0x74,0x20,0x61,0x6e,0x20,0x75,
|
||||
0x6e,0x64,0x65,0x72,0x6c,0x79,0x69,0x6e,0x67,0x20,0x4f,0x53,0x2e,0x0d,0x0a,0x09,
|
||||
0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,0x0a,
|
||||
0x09,0x20,0x20,0x20,0x20,0x54,0x68,0x65,0x20,0x66,0x6f,0x63,0x75,0x73,0x20,0x6f,
|
||||
0x66,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x54,0x43,0x50,0x2f,0x49,
|
||||
0x50,0x20,0x69,0x6d,0x70,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x61,0x74,0x69,0x6f,0x6e,
|
||||
0x20,0x69,0x73,0x20,0x74,0x6f,0x20,0x72,0x65,0x64,0x75,0x63,0x65,0x0d,0x0a,0x09,
|
||||
0x20,0x20,0x20,0x20,0x74,0x68,0x65,0x20,0x52,0x41,0x4d,0x20,0x75,0x73,0x61,0x67,
|
||||
0x65,0x20,0x77,0x68,0x69,0x6c,0x65,0x20,0x73,0x74,0x69,0x6c,0x6c,0x20,0x68,0x61,
|
||||
0x76,0x69,0x6e,0x67,0x20,0x61,0x20,0x66,0x75,0x6c,0x6c,0x20,0x73,0x63,0x61,0x6c,
|
||||
0x65,0x20,0x54,0x43,0x50,0x2e,0x20,0x54,0x68,0x69,0x73,0x0d,0x0a,0x09,0x20,0x20,
|
||||
0x20,0x20,0x6d,0x61,0x6b,0x65,0x73,0x20,0x6c,0x77,0x49,0x50,0x20,0x73,0x75,0x69,
|
||||
0x74,0x61,0x62,0x6c,0x65,0x20,0x66,0x6f,0x72,0x20,0x75,0x73,0x65,0x20,0x69,0x6e,
|
||||
0x20,0x65,0x6d,0x62,0x65,0x64,0x64,0x65,0x64,0x20,0x73,0x79,0x73,0x74,0x65,0x6d,
|
||||
0x73,0x20,0x77,0x69,0x74,0x68,0x20,0x74,0x65,0x6e,0x73,0x0d,0x0a,0x09,0x20,0x20,
|
||||
0x20,0x20,0x6f,0x66,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x20,0x6f,
|
||||
0x66,0x20,0x66,0x72,0x65,0x65,0x20,0x52,0x41,0x4d,0x20,0x61,0x6e,0x64,0x20,0x72,
|
||||
0x6f,0x6f,0x6d,0x20,0x66,0x6f,0x72,0x20,0x61,0x72,0x6f,0x75,0x6e,0x64,0x20,0x34,
|
||||
0x30,0x20,0x6b,0x69,0x6c,0x6f,0x62,0x79,0x74,0x65,0x73,0x0d,0x0a,0x09,0x20,0x20,
|
||||
0x20,0x20,0x6f,0x66,0x20,0x63,0x6f,0x64,0x65,0x20,0x52,0x4f,0x4d,0x2e,0x0d,0x0a,
|
||||
0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x70,0x3e,0x0d,
|
||||
0x0a,0x09,0x20,0x20,0x20,0x20,0x4d,0x6f,0x72,0x65,0x20,0x69,0x6e,0x66,0x6f,0x72,
|
||||
0x6d,0x61,0x74,0x69,0x6f,0x6e,0x20,0x61,0x62,0x6f,0x75,0x74,0x20,0x6c,0x77,0x49,
|
||||
0x50,0x20,0x63,0x61,0x6e,0x20,0x62,0x65,0x20,0x66,0x6f,0x75,0x6e,0x64,0x20,0x61,
|
||||
0x74,0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x0d,0x0a,0x09,0x20,0x20,0x20,
|
||||
0x20,0x68,0x6f,0x6d,0x65,0x70,0x61,0x67,0x65,0x20,0x61,0x74,0x20,0x3c,0x61,0x0d,
|
||||
0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,0x22,0x68,0x74,0x74,0x70,
|
||||
0x3a,0x2f,0x2f,0x73,0x61,0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,
|
||||
0x6e,0x75,0x2e,0x6f,0x72,0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,
|
||||
0x6c,0x77,0x69,0x70,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x61,
|
||||
0x76,0x61,0x6e,0x6e,0x61,0x68,0x2e,0x6e,0x6f,0x6e,0x67,0x6e,0x75,0x2e,0x6f,0x72,
|
||||
0x67,0x2f,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x73,0x2f,0x6c,0x77,0x69,0x70,0x2f,
|
||||
0x3c,0x2f,0x61,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x6f,0x72,0x20,0x61,0x74,
|
||||
0x20,0x74,0x68,0x65,0x20,0x6c,0x77,0x49,0x50,0x20,0x77,0x69,0x6b,0x69,0x20,0x61,
|
||||
0x74,0x20,0x3c,0x61,0x0d,0x0a,0x09,0x20,0x20,0x20,0x20,0x68,0x72,0x65,0x66,0x3d,
|
||||
0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b,
|
||||
0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x22,0x3e,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,
|
||||
0x6c,0x77,0x69,0x70,0x2e,0x77,0x69,0x6b,0x69,0x61,0x2e,0x63,0x6f,0x6d,0x2f,0x3c,
|
||||
0x2f,0x61,0x3e,0x2e,0x0d,0x0a,0x09,0x20,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a,0x09,
|
||||
0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x74,0x64,0x3e,0x0d,0x0a,0x09,0x20,0x20,0x26,0x6e,
|
||||
0x62,0x73,0x70,0x3b,0x0d,0x0a,0x09,0x3c,0x2f,0x74,0x64,0x3e,0x3c,0x2f,0x74,0x72,
|
||||
0x3e,0x0d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x3c,0x2f,0x74,0x61,0x62,0x6c,0x65,
|
||||
0x3e,0x0d,0x0a,0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x0d,0x0a,0x3c,0x2f,0x68,0x74,
|
||||
0x6d,0x6c,0x3e,0x0d,0x0a,0x0d,0x0a,};
|
||||
|
||||
|
||||
|
||||
const struct fsdata_file file__img_sics_gif[] = { {
|
||||
file_NULL,
|
||||
data__img_sics_gif,
|
||||
data__img_sics_gif + 16,
|
||||
sizeof(data__img_sics_gif) - 16,
|
||||
1,
|
||||
}};
|
||||
|
||||
const struct fsdata_file file__404_html[] = { {
|
||||
file__img_sics_gif,
|
||||
data__404_html,
|
||||
data__404_html + 12,
|
||||
sizeof(data__404_html) - 12,
|
||||
1,
|
||||
}};
|
||||
|
||||
const struct fsdata_file file__index_html[] = { {
|
||||
file__404_html,
|
||||
data__index_html,
|
||||
data__index_html + 12,
|
||||
sizeof(data__index_html) - 12,
|
||||
1,
|
||||
}};
|
||||
|
||||
#define FS_ROOT file__index_html
|
||||
#define FS_NUMFILES 3
|
||||
|
||||
@@ -29,11 +29,22 @@
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
#ifndef __NETIF_LOOPIF_H__
|
||||
#define __NETIF_LOOPIF_H__
|
||||
#ifndef LWIP_FSDATA_H
|
||||
#define LWIP_FSDATA_H
|
||||
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/apps/httpd_opts.h"
|
||||
#include "lwip/apps/fs.h"
|
||||
|
||||
err_t loopif_init(struct netif *netif);
|
||||
struct fsdata_file {
|
||||
const struct fsdata_file *next;
|
||||
const unsigned char *name;
|
||||
const unsigned char *data;
|
||||
int len;
|
||||
u8_t flags;
|
||||
#if HTTPD_PRECALCULATED_CHECKSUM
|
||||
u16_t chksum_count;
|
||||
const struct fsdata_chksum *chksum;
|
||||
#endif /* HTTPD_PRECALCULATED_CHECKSUM */
|
||||
};
|
||||
|
||||
#endif /* __NETIF_LOOPIF_H__ */
|
||||
#endif /* LWIP_FSDATA_H */
|
||||
2619
src/apps/httpd/httpd.c
Normal file
2619
src/apps/httpd/httpd.c
Normal file
File diff suppressed because it is too large
Load Diff
114
src/apps/httpd/httpd_structs.h
Normal file
114
src/apps/httpd/httpd_structs.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#ifndef LWIP_HTTPD_STRUCTS_H
|
||||
#define LWIP_HTTPD_STRUCTS_H
|
||||
|
||||
#include "lwip/apps/httpd.h"
|
||||
|
||||
#if LWIP_HTTPD_DYNAMIC_HEADERS
|
||||
/** This struct is used for a list of HTTP header strings for various
|
||||
* filename extensions. */
|
||||
typedef struct
|
||||
{
|
||||
const char *extension;
|
||||
const char *content_type;
|
||||
} tHTTPHeader;
|
||||
|
||||
/** A list of strings used in HTTP headers (see RFC 1945 HTTP/1.0 and
|
||||
* RFC 2616 HTTP/1.1 for header field definitions) */
|
||||
static const char * const g_psHTTPHeaderStrings[] =
|
||||
{
|
||||
"HTTP/1.0 200 OK\r\n",
|
||||
"HTTP/1.0 404 File not found\r\n",
|
||||
"HTTP/1.0 400 Bad Request\r\n",
|
||||
"HTTP/1.0 501 Not Implemented\r\n",
|
||||
"HTTP/1.1 200 OK\r\n",
|
||||
"HTTP/1.1 404 File not found\r\n",
|
||||
"HTTP/1.1 400 Bad Request\r\n",
|
||||
"HTTP/1.1 501 Not Implemented\r\n",
|
||||
"Content-Length: ",
|
||||
"Connection: Close\r\n",
|
||||
"Connection: keep-alive\r\n",
|
||||
"Connection: keep-alive\r\nContent-Length: ",
|
||||
"Server: "HTTPD_SERVER_AGENT"\r\n",
|
||||
"\r\n<html><body><h2>404: The requested file cannot be found.</h2></body></html>\r\n"
|
||||
#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
|
||||
,"Connection: keep-alive\r\nContent-Length: 77\r\n\r\n<html><body><h2>404: The requested file cannot be found.</h2></body></html>\r\n"
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Indexes into the g_psHTTPHeaderStrings array */
|
||||
#define HTTP_HDR_OK 0 /* 200 OK */
|
||||
#define HTTP_HDR_NOT_FOUND 1 /* 404 File not found */
|
||||
#define HTTP_HDR_BAD_REQUEST 2 /* 400 Bad request */
|
||||
#define HTTP_HDR_NOT_IMPL 3 /* 501 Not Implemented */
|
||||
#define HTTP_HDR_OK_11 4 /* 200 OK */
|
||||
#define HTTP_HDR_NOT_FOUND_11 5 /* 404 File not found */
|
||||
#define HTTP_HDR_BAD_REQUEST_11 6 /* 400 Bad request */
|
||||
#define HTTP_HDR_NOT_IMPL_11 7 /* 501 Not Implemented */
|
||||
#define HTTP_HDR_CONTENT_LENGTH 8 /* Content-Length: (HTTP 1.0)*/
|
||||
#define HTTP_HDR_CONN_CLOSE 9 /* Connection: Close (HTTP 1.1) */
|
||||
#define HTTP_HDR_CONN_KEEPALIVE 10 /* Connection: keep-alive (HTTP 1.1) */
|
||||
#define HTTP_HDR_KEEPALIVE_LEN 11 /* Connection: keep-alive + Content-Length: (HTTP 1.1)*/
|
||||
#define HTTP_HDR_SERVER 12 /* Server: HTTPD_SERVER_AGENT */
|
||||
#define DEFAULT_404_HTML 13 /* default 404 body */
|
||||
#if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
|
||||
#define DEFAULT_404_HTML_PERSISTENT 14 /* default 404 body, but including Connection: keep-alive */
|
||||
#endif
|
||||
|
||||
|
||||
#define HTTP_HDR_HTML "Content-type: text/html\r\n\r\n"
|
||||
#define HTTP_HDR_SSI "Content-type: text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache\r\n\r\n"
|
||||
#define HTTP_HDR_GIF "Content-type: image/gif\r\n\r\n"
|
||||
#define HTTP_HDR_PNG "Content-type: image/png\r\n\r\n"
|
||||
#define HTTP_HDR_JPG "Content-type: image/jpeg\r\n\r\n"
|
||||
#define HTTP_HDR_BMP "Content-type: image/bmp\r\n\r\n"
|
||||
#define HTTP_HDR_ICO "Content-type: image/x-icon\r\n\r\n"
|
||||
#define HTTP_HDR_APP "Content-type: application/octet-stream\r\n\r\n"
|
||||
#define HTTP_HDR_JS "Content-type: application/javascript\r\n\r\n"
|
||||
#define HTTP_HDR_RA "Content-type: application/javascript\r\n\r\n"
|
||||
#define HTTP_HDR_CSS "Content-type: text/css\r\n\r\n"
|
||||
#define HTTP_HDR_SWF "Content-type: application/x-shockwave-flash\r\n\r\n"
|
||||
#define HTTP_HDR_XML "Content-type: text/xml\r\n\r\n"
|
||||
#define HTTP_HDR_PDF "Content-type: application/pdf\r\n\r\n"
|
||||
#define HTTP_HDR_JSON "Content-type: application/json\r\n\r\n"
|
||||
|
||||
#define HTTP_HDR_DEFAULT_TYPE "Content-type: text/plain\r\n\r\n"
|
||||
|
||||
/** A list of extension-to-HTTP header strings (see outdated RFC 1700 MEDIA TYPES
|
||||
* and http://www.iana.org/assignments/media-types for registered content types
|
||||
* and subtypes) */
|
||||
static const tHTTPHeader g_psHTTPHeaders[] =
|
||||
{
|
||||
{ "html", HTTP_HDR_HTML},
|
||||
{ "htm", HTTP_HDR_HTML},
|
||||
{ "shtml",HTTP_HDR_SSI},
|
||||
{ "shtm", HTTP_HDR_SSI},
|
||||
{ "ssi", HTTP_HDR_SSI},
|
||||
{ "gif", HTTP_HDR_GIF},
|
||||
{ "png", HTTP_HDR_PNG},
|
||||
{ "jpg", HTTP_HDR_JPG},
|
||||
{ "bmp", HTTP_HDR_BMP},
|
||||
{ "ico", HTTP_HDR_ICO},
|
||||
{ "class",HTTP_HDR_APP},
|
||||
{ "cls", HTTP_HDR_APP},
|
||||
{ "js", HTTP_HDR_JS},
|
||||
{ "ram", HTTP_HDR_RA},
|
||||
{ "css", HTTP_HDR_CSS},
|
||||
{ "swf", HTTP_HDR_SWF},
|
||||
{ "xml", HTTP_HDR_XML},
|
||||
{ "xsl", HTTP_HDR_XML},
|
||||
{ "pdf", HTTP_HDR_PDF},
|
||||
{ "json", HTTP_HDR_JSON}
|
||||
};
|
||||
|
||||
#define NUM_HTTP_HEADERS (sizeof(g_psHTTPHeaders) / sizeof(tHTTPHeader))
|
||||
|
||||
#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
|
||||
|
||||
#if LWIP_HTTPD_SSI
|
||||
static const char * const g_pcSSIExtensions[] = {
|
||||
".shtml", ".shtm", ".ssi", ".xml"
|
||||
};
|
||||
#define NUM_SHTML_EXTENSIONS (sizeof(g_pcSSIExtensions) / sizeof(const char *))
|
||||
#endif /* LWIP_HTTPD_SSI */
|
||||
|
||||
#endif /* LWIP_HTTPD_STRUCTS_H */
|
||||
97
src/apps/httpd/makefsdata/makefsdata
Normal file
97
src/apps/httpd/makefsdata/makefsdata
Normal file
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
open(OUTPUT, "> fsdata.c");
|
||||
|
||||
chdir("fs");
|
||||
open(FILES, "find . -type f |");
|
||||
|
||||
while($file = <FILES>) {
|
||||
|
||||
# Do not include files in CVS directories nor backup files.
|
||||
if($file =~ /(CVS|~)/) {
|
||||
next;
|
||||
}
|
||||
|
||||
chop($file);
|
||||
|
||||
open(HEADER, "> /tmp/header") || die $!;
|
||||
if($file =~ /404/) {
|
||||
print(HEADER "HTTP/1.0 404 File not found\r\n");
|
||||
} else {
|
||||
print(HEADER "HTTP/1.0 200 OK\r\n");
|
||||
}
|
||||
print(HEADER "Server: lwIP/pre-0.6 (http://www.sics.se/~adam/lwip/)\r\n");
|
||||
if($file =~ /\.html$/) {
|
||||
print(HEADER "Content-type: text/html\r\n");
|
||||
} elsif($file =~ /\.gif$/) {
|
||||
print(HEADER "Content-type: image/gif\r\n");
|
||||
} elsif($file =~ /\.png$/) {
|
||||
print(HEADER "Content-type: image/png\r\n");
|
||||
} elsif($file =~ /\.jpg$/) {
|
||||
print(HEADER "Content-type: image/jpeg\r\n");
|
||||
} elsif($file =~ /\.class$/) {
|
||||
print(HEADER "Content-type: application/octet-stream\r\n");
|
||||
} elsif($file =~ /\.ram$/) {
|
||||
print(HEADER "Content-type: audio/x-pn-realaudio\r\n");
|
||||
} else {
|
||||
print(HEADER "Content-type: text/plain\r\n");
|
||||
}
|
||||
print(HEADER "\r\n");
|
||||
close(HEADER);
|
||||
|
||||
unless($file =~ /\.plain$/ || $file =~ /cgi/) {
|
||||
system("cat /tmp/header $file > /tmp/file");
|
||||
} else {
|
||||
system("cp $file /tmp/file");
|
||||
}
|
||||
|
||||
open(FILE, "/tmp/file");
|
||||
unlink("/tmp/file");
|
||||
unlink("/tmp/header");
|
||||
|
||||
$file =~ s/\.//;
|
||||
$fvar = $file;
|
||||
$fvar =~ s-/-_-g;
|
||||
$fvar =~ s-\.-_-g;
|
||||
print(OUTPUT "static const unsigned char data".$fvar."[] = {\n");
|
||||
print(OUTPUT "\t/* $file */\n\t");
|
||||
for($j = 0; $j < length($file); $j++) {
|
||||
printf(OUTPUT "%#02x, ", unpack("C", substr($file, $j, 1)));
|
||||
}
|
||||
printf(OUTPUT "0,\n");
|
||||
|
||||
|
||||
$i = 0;
|
||||
while(read(FILE, $data, 1)) {
|
||||
if($i == 0) {
|
||||
print(OUTPUT "\t");
|
||||
}
|
||||
printf(OUTPUT "%#02x, ", unpack("C", $data));
|
||||
$i++;
|
||||
if($i == 10) {
|
||||
print(OUTPUT "\n");
|
||||
$i = 0;
|
||||
}
|
||||
}
|
||||
print(OUTPUT "};\n\n");
|
||||
close(FILE);
|
||||
push(@fvars, $fvar);
|
||||
push(@files, $file);
|
||||
}
|
||||
|
||||
for($i = 0; $i < @fvars; $i++) {
|
||||
$file = $files[$i];
|
||||
$fvar = $fvars[$i];
|
||||
|
||||
if($i == 0) {
|
||||
$prevfile = "NULL";
|
||||
} else {
|
||||
$prevfile = "file" . $fvars[$i - 1];
|
||||
}
|
||||
print(OUTPUT "const struct fsdata_file file".$fvar."[] = {{$prevfile, data$fvar, ");
|
||||
print(OUTPUT "data$fvar + ". (length($file) + 1) .", ");
|
||||
print(OUTPUT "sizeof(data$fvar) - ". (length($file) + 1) ."}};\n\n");
|
||||
}
|
||||
|
||||
print(OUTPUT "#define FS_ROOT file$fvars[$i - 1]\n\n");
|
||||
print(OUTPUT "#define FS_NUMFILES $i\n");
|
||||
1033
src/apps/httpd/makefsdata/makefsdata.c
Normal file
1033
src/apps/httpd/makefsdata/makefsdata.c
Normal file
File diff suppressed because it is too large
Load Diff
13
src/apps/httpd/makefsdata/readme.txt
Normal file
13
src/apps/httpd/makefsdata/readme.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
This directory contains a script ('makefsdata') to create C code suitable for
|
||||
httpd for given html pages (or other files) in a directory.
|
||||
|
||||
There is also a plain C console application doing the same and extended a bit.
|
||||
|
||||
Usage: htmlgen [targetdir] [-s] [-i]s
|
||||
targetdir: relative or absolute path to files to convert
|
||||
switch -s: toggle processing of subdirectories (default is on)
|
||||
switch -e: exclude HTTP header from file (header is created at runtime, default is on)
|
||||
switch -11: include HTTP 1.1 header (1.0 is default)
|
||||
|
||||
if targetdir not specified, makefsdata will attempt to
|
||||
process files in subdirectory 'fs'.
|
||||
663
src/apps/lwiperf/lwiperf.c
Normal file
663
src/apps/lwiperf/lwiperf.c
Normal file
@@ -0,0 +1,663 @@
|
||||
/**
|
||||
* @file
|
||||
* lwIP iPerf server implementation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup iperf Iperf server
|
||||
* @ingroup apps
|
||||
*
|
||||
* This is a simple performance measuring server to check your bandwith using
|
||||
* iPerf2 on a PC as client.
|
||||
* It is currently a minimal implementation providing an IPv4 TCP server only.
|
||||
*
|
||||
* @todo: implement UDP mode and IPv6
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2014 Simon Goldschmidt
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Simon Goldschmidt
|
||||
*/
|
||||
|
||||
#include "lwip/apps/lwiperf.h"
|
||||
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* Currently, only TCP-over-IPv4 is implemented (does iperf support IPv6 anyway?) */
|
||||
#if LWIP_IPV4 && LWIP_TCP
|
||||
|
||||
/** Specify the idle timeout (in seconds) after that the test fails */
|
||||
#ifndef LWIPERF_TCP_MAX_IDLE_SEC
|
||||
#define LWIPERF_TCP_MAX_IDLE_SEC 10U
|
||||
#endif
|
||||
#if LWIPERF_TCP_MAX_IDLE_SEC > 255
|
||||
#error LWIPERF_TCP_MAX_IDLE_SEC must fit into an u8_t
|
||||
#endif
|
||||
|
||||
/* File internal memory allocation (struct lwiperf_*): this defaults to
|
||||
the heap */
|
||||
#ifndef LWIPERF_ALLOC
|
||||
#define LWIPERF_ALLOC(type) mem_malloc(sizeof(type))
|
||||
#define LWIPERF_FREE(type, item) mem_free(item)
|
||||
#endif
|
||||
|
||||
/** If this is 1, check that received data has the correct format */
|
||||
#ifndef LWIPERF_CHECK_RX_DATA
|
||||
#define LWIPERF_CHECK_RX_DATA 0
|
||||
#endif
|
||||
|
||||
/** This is the Iperf settings struct sent from the client */
|
||||
typedef struct _lwiperf_settings {
|
||||
#define LWIPERF_FLAGS_ANSWER_TEST 0x80000000
|
||||
#define LWIPERF_FLAGS_ANSWER_NOW 0x00000001
|
||||
u32_t flags;
|
||||
u32_t num_threads; /* unused for now */
|
||||
u32_t remote_port;
|
||||
u32_t buffer_len; /* unused for now */
|
||||
u32_t win_band; /* TCP window / UDP rate: unused for now */
|
||||
u32_t amount; /* pos. value: bytes?; neg. values: time (unit is 10ms: 1/100 second) */
|
||||
} lwiperf_settings_t;
|
||||
|
||||
/** Basic connection handle */
|
||||
struct _lwiperf_state_base;
|
||||
typedef struct _lwiperf_state_base lwiperf_state_base_t;
|
||||
struct _lwiperf_state_base {
|
||||
/* 1=tcp, 0=udp */
|
||||
u8_t tcp;
|
||||
/* 1=server, 0=client */
|
||||
u8_t server;
|
||||
lwiperf_state_base_t* next;
|
||||
lwiperf_state_base_t* related_server_state;
|
||||
};
|
||||
|
||||
/** Connection handle for a TCP iperf session */
|
||||
typedef struct _lwiperf_state_tcp {
|
||||
lwiperf_state_base_t base;
|
||||
struct tcp_pcb* server_pcb;
|
||||
struct tcp_pcb* conn_pcb;
|
||||
u32_t time_started;
|
||||
lwiperf_report_fn report_fn;
|
||||
void* report_arg;
|
||||
u8_t poll_count;
|
||||
u8_t next_num;
|
||||
u32_t bytes_transferred;
|
||||
lwiperf_settings_t settings;
|
||||
u8_t have_settings_buf;
|
||||
} lwiperf_state_tcp_t;
|
||||
|
||||
/** List of active iperf sessions */
|
||||
static lwiperf_state_base_t* lwiperf_all_connections;
|
||||
/** A const buffer to send from: we want to measure sending, not copying! */
|
||||
static const u8_t lwiperf_txbuf_const[1600] = {
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
'0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9',
|
||||
};
|
||||
|
||||
static err_t lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb);
|
||||
static void lwiperf_tcp_err(void *arg, err_t err);
|
||||
|
||||
/** Add an iperf session to the 'active' list */
|
||||
static void
|
||||
lwiperf_list_add(lwiperf_state_base_t* item)
|
||||
{
|
||||
if (lwiperf_all_connections == NULL) {
|
||||
lwiperf_all_connections = item;
|
||||
} else {
|
||||
item = lwiperf_all_connections;
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove an iperf session from the 'active' list */
|
||||
static void
|
||||
lwiperf_list_remove(lwiperf_state_base_t* item)
|
||||
{
|
||||
lwiperf_state_base_t* prev = NULL;
|
||||
lwiperf_state_base_t* iter;
|
||||
for (iter = lwiperf_all_connections; iter != NULL; prev = iter, iter = iter->next) {
|
||||
if (iter == item) {
|
||||
if (prev == NULL) {
|
||||
lwiperf_all_connections = iter->next;
|
||||
} else {
|
||||
prev->next = item;
|
||||
}
|
||||
/* @debug: ensure this item is listed only once */
|
||||
for (iter = iter->next; iter != NULL; iter = iter->next) {
|
||||
LWIP_ASSERT("duplicate entry", iter != item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Call the report function of an iperf tcp session */
|
||||
static void
|
||||
lwip_tcp_conn_report(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type)
|
||||
{
|
||||
if ((conn != NULL) && (conn->report_fn != NULL)) {
|
||||
u32_t now, duration_ms, bandwidth_kbitpsec;
|
||||
now = sys_now();
|
||||
duration_ms = now - conn->time_started;
|
||||
if (duration_ms == 0) {
|
||||
bandwidth_kbitpsec = 0;
|
||||
} else {
|
||||
bandwidth_kbitpsec = (conn->bytes_transferred / duration_ms) * 8U;
|
||||
}
|
||||
conn->report_fn(conn->report_arg, report_type,
|
||||
&conn->conn_pcb->local_ip, conn->conn_pcb->local_port,
|
||||
&conn->conn_pcb->remote_ip, conn->conn_pcb->remote_port,
|
||||
conn->bytes_transferred, duration_ms, bandwidth_kbitpsec);
|
||||
}
|
||||
}
|
||||
|
||||
/** Close an iperf tcp session */
|
||||
static void
|
||||
lwiperf_tcp_close(lwiperf_state_tcp_t* conn, enum lwiperf_report_type report_type)
|
||||
{
|
||||
err_t err;
|
||||
|
||||
lwip_tcp_conn_report(conn, report_type);
|
||||
lwiperf_list_remove(&conn->base);
|
||||
if (conn->conn_pcb != NULL) {
|
||||
tcp_arg(conn->conn_pcb, NULL);
|
||||
tcp_poll(conn->conn_pcb, NULL, 0);
|
||||
tcp_sent(conn->conn_pcb, NULL);
|
||||
tcp_recv(conn->conn_pcb, NULL);
|
||||
tcp_err(conn->conn_pcb, NULL);
|
||||
err = tcp_close(conn->conn_pcb);
|
||||
if (err != ERR_OK) {
|
||||
/* don't want to wait for free memory here... */
|
||||
tcp_abort(conn->conn_pcb);
|
||||
}
|
||||
} else {
|
||||
/* no conn pcb, this is the server pcb */
|
||||
err = tcp_close(conn->server_pcb);
|
||||
LWIP_ASSERT("error", err != ERR_OK);
|
||||
}
|
||||
LWIPERF_FREE(lwiperf_state_tcp_t, conn);
|
||||
}
|
||||
|
||||
/** Try to send more data on an iperf tcp session */
|
||||
static err_t
|
||||
lwiperf_tcp_client_send_more(lwiperf_state_tcp_t* conn)
|
||||
{
|
||||
int send_more;
|
||||
err_t err;
|
||||
u16_t txlen;
|
||||
u16_t txlen_max;
|
||||
void* txptr;
|
||||
u8_t apiflags;
|
||||
|
||||
LWIP_ASSERT("conn invalid", (conn != NULL) && conn->base.tcp && (conn->base.server == 0));
|
||||
|
||||
do {
|
||||
send_more = 0;
|
||||
if (conn->settings.amount & PP_HTONL(0x80000000)) {
|
||||
/* this session is time-limited */
|
||||
u32_t now = sys_now();
|
||||
u32_t diff_ms = now - conn->time_started;
|
||||
u32_t time = (u32_t)-(s32_t)lwip_htonl(conn->settings.amount);
|
||||
u32_t time_ms = time * 10;
|
||||
if (diff_ms >= time_ms) {
|
||||
/* time specified by the client is over -> close the connection */
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
|
||||
return ERR_OK;
|
||||
}
|
||||
} else {
|
||||
/* this session is byte-limited */
|
||||
u32_t amount_bytes = lwip_htonl(conn->settings.amount);
|
||||
/* @todo: this can send up to 1*MSS more than requested... */
|
||||
if (amount_bytes >= conn->bytes_transferred) {
|
||||
/* all requested bytes transferred -> close the connection */
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_CLIENT);
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->bytes_transferred < 24) {
|
||||
/* transmit the settings a first time */
|
||||
txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred];
|
||||
txlen_max = (u16_t)(24 - conn->bytes_transferred);
|
||||
apiflags = TCP_WRITE_FLAG_COPY;
|
||||
} else if (conn->bytes_transferred < 48) {
|
||||
/* transmit the settings a second time */
|
||||
txptr = &((u8_t*)&conn->settings)[conn->bytes_transferred - 24];
|
||||
txlen_max = (u16_t)(48 - conn->bytes_transferred);
|
||||
apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;
|
||||
send_more = 1;
|
||||
} else {
|
||||
/* transmit data */
|
||||
/* @todo: every x bytes, transmit the settings again */
|
||||
txptr = (void*)(size_t)&lwiperf_txbuf_const[conn->bytes_transferred % 10];
|
||||
txlen_max = TCP_MSS;
|
||||
if (conn->bytes_transferred == 48) { /* @todo: fix this for intermediate settings, too */
|
||||
txlen_max = TCP_MSS - 24;
|
||||
}
|
||||
apiflags = 0; /* no copying needed */
|
||||
send_more = 1;
|
||||
}
|
||||
txlen = txlen_max;
|
||||
do {
|
||||
err = tcp_write(conn->conn_pcb, txptr, txlen, apiflags);
|
||||
if (err == ERR_MEM) {
|
||||
txlen /= 2;
|
||||
}
|
||||
} while ((err == ERR_MEM) && (txlen >= (TCP_MSS/2)));
|
||||
|
||||
if (err == ERR_OK) {
|
||||
conn->bytes_transferred += txlen;
|
||||
} else {
|
||||
send_more = 0;
|
||||
}
|
||||
} while(send_more);
|
||||
|
||||
tcp_output(conn->conn_pcb);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** TCP sent callback, try to send more data */
|
||||
static err_t
|
||||
lwiperf_tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
|
||||
{
|
||||
lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
|
||||
/* @todo: check 'len' (e.g. to time ACK of all data)? for now, we just send more... */
|
||||
LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
|
||||
LWIP_UNUSED_ARG(tpcb);
|
||||
LWIP_UNUSED_ARG(len);
|
||||
|
||||
conn->poll_count = 0;
|
||||
|
||||
return lwiperf_tcp_client_send_more(conn);
|
||||
}
|
||||
|
||||
/** TCP connected callback (active connection), send data now */
|
||||
static err_t
|
||||
lwiperf_tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
|
||||
{
|
||||
lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
|
||||
LWIP_ASSERT("invalid conn", conn->conn_pcb == tpcb);
|
||||
LWIP_UNUSED_ARG(tpcb);
|
||||
if (err != ERR_OK) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
|
||||
return ERR_OK;
|
||||
}
|
||||
conn->poll_count = 0;
|
||||
conn->time_started = sys_now();
|
||||
return lwiperf_tcp_client_send_more(conn);
|
||||
}
|
||||
|
||||
/** Start TCP connection back to the client (either parallel or after the
|
||||
* receive test has finished.
|
||||
*/
|
||||
static err_t
|
||||
lwiperf_tx_start(lwiperf_state_tcp_t* conn)
|
||||
{
|
||||
err_t err;
|
||||
lwiperf_state_tcp_t* client_conn;
|
||||
struct tcp_pcb* newpcb;
|
||||
ip_addr_t remote_addr;
|
||||
u16_t remote_port;
|
||||
|
||||
client_conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
|
||||
if (client_conn == NULL) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
newpcb = tcp_new();
|
||||
if (newpcb == NULL) {
|
||||
LWIPERF_FREE(lwiperf_state_tcp_t, client_conn);
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
MEMCPY(client_conn, conn, sizeof(lwiperf_state_tcp_t));
|
||||
client_conn->base.server = 0;
|
||||
client_conn->server_pcb = NULL;
|
||||
client_conn->conn_pcb = newpcb;
|
||||
client_conn->time_started = sys_now(); /* @todo: set this again on 'connected' */
|
||||
client_conn->poll_count = 0;
|
||||
client_conn->next_num = 4; /* initial nr is '4' since the header has 24 byte */
|
||||
client_conn->bytes_transferred = 0;
|
||||
client_conn->settings.flags = 0; /* prevent the remote side starting back as client again */
|
||||
|
||||
tcp_arg(newpcb, client_conn);
|
||||
tcp_sent(newpcb, lwiperf_tcp_client_sent);
|
||||
tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
|
||||
tcp_err(newpcb, lwiperf_tcp_err);
|
||||
|
||||
ip_addr_copy(remote_addr, conn->conn_pcb->remote_ip);
|
||||
remote_port = (u16_t)lwip_htonl(client_conn->settings.remote_port);
|
||||
|
||||
err = tcp_connect(newpcb, &remote_addr, remote_port, lwiperf_tcp_client_connected);
|
||||
if (err != ERR_OK) {
|
||||
lwiperf_tcp_close(client_conn, LWIPERF_TCP_ABORTED_LOCAL);
|
||||
return err;
|
||||
}
|
||||
lwiperf_list_add(&client_conn->base);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** Receive data on an iperf tcp session */
|
||||
static err_t
|
||||
lwiperf_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
|
||||
{
|
||||
u8_t tmp;
|
||||
u16_t tot_len;
|
||||
u32_t packet_idx;
|
||||
struct pbuf* q;
|
||||
lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
|
||||
|
||||
LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
|
||||
LWIP_UNUSED_ARG(tpcb);
|
||||
|
||||
if (err != ERR_OK) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
|
||||
return ERR_OK;
|
||||
}
|
||||
if (p == NULL) {
|
||||
/* connection closed -> test done */
|
||||
if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) ==
|
||||
PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST)) {
|
||||
/* client requested transmission after end of test */
|
||||
lwiperf_tx_start(conn);
|
||||
}
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_DONE_SERVER);
|
||||
return ERR_OK;
|
||||
}
|
||||
tot_len = p->tot_len;
|
||||
|
||||
conn->poll_count = 0;
|
||||
|
||||
if ((!conn->have_settings_buf) || ((conn->bytes_transferred -24) % (1024*128) == 0)) {
|
||||
/* wait for 24-byte header */
|
||||
if (p->tot_len < sizeof(lwiperf_settings_t)) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
|
||||
pbuf_free(p);
|
||||
return ERR_VAL;
|
||||
}
|
||||
if (!conn->have_settings_buf) {
|
||||
if (pbuf_copy_partial(p, &conn->settings, sizeof(lwiperf_settings_t), 0) != sizeof(lwiperf_settings_t)) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
|
||||
pbuf_free(p);
|
||||
return ERR_VAL;
|
||||
}
|
||||
conn->have_settings_buf = 1;
|
||||
if ((conn->settings.flags & PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) ==
|
||||
PP_HTONL(LWIPERF_FLAGS_ANSWER_TEST|LWIPERF_FLAGS_ANSWER_NOW)) {
|
||||
/* client requested parallel transmission test */
|
||||
err_t err2 = lwiperf_tx_start(conn);
|
||||
if (err2 != ERR_OK) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_TXERROR);
|
||||
pbuf_free(p);
|
||||
return err2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (pbuf_memcmp(p, 0, &conn->settings, sizeof(lwiperf_settings_t)) != 0) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
|
||||
pbuf_free(p);
|
||||
return ERR_VAL;
|
||||
}
|
||||
}
|
||||
conn->bytes_transferred += sizeof(lwiperf_settings_t);
|
||||
if (conn->bytes_transferred <= 24) {
|
||||
conn->time_started = sys_now();
|
||||
tcp_recved(tpcb, p->tot_len);
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
conn->next_num = 4; /* 24 bytes received... */
|
||||
tmp = pbuf_header(p, -24);
|
||||
LWIP_ASSERT("pbuf_header failed", tmp == 0);
|
||||
}
|
||||
|
||||
packet_idx = 0;
|
||||
for (q = p; q != NULL; q = q->next) {
|
||||
#if LWIPERF_CHECK_RX_DATA
|
||||
const u8_t* payload = (const u8_t*)q->payload;
|
||||
u16_t i;
|
||||
for (i = 0; i < q->len; i++) {
|
||||
u8_t val = payload[i];
|
||||
u8_t num = val - '0';
|
||||
if (num == conn->next_num) {
|
||||
conn->next_num++;
|
||||
if (conn->next_num == 10) {
|
||||
conn->next_num = 0;
|
||||
}
|
||||
} else {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL_DATAERROR);
|
||||
pbuf_free(p);
|
||||
return ERR_VAL;
|
||||
}
|
||||
}
|
||||
packet_idx += i;
|
||||
#else
|
||||
packet_idx += q->len;
|
||||
#endif
|
||||
}
|
||||
LWIP_ASSERT("count mismatch", packet_idx == p->tot_len);
|
||||
conn->bytes_transferred += packet_idx;
|
||||
tcp_recved(tpcb, tot_len);
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** Error callback, iperf tcp session aborted */
|
||||
static void
|
||||
lwiperf_tcp_err(void *arg, err_t err)
|
||||
{
|
||||
lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
|
||||
LWIP_UNUSED_ARG(err);
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_REMOTE);
|
||||
}
|
||||
|
||||
/** TCP poll callback, try to send more data */
|
||||
static err_t
|
||||
lwiperf_tcp_poll(void *arg, struct tcp_pcb *tpcb)
|
||||
{
|
||||
lwiperf_state_tcp_t* conn = (lwiperf_state_tcp_t*)arg;
|
||||
LWIP_ASSERT("pcb mismatch", conn->conn_pcb == tpcb);
|
||||
LWIP_UNUSED_ARG(tpcb);
|
||||
if (++conn->poll_count >= LWIPERF_TCP_MAX_IDLE_SEC) {
|
||||
lwiperf_tcp_close(conn, LWIPERF_TCP_ABORTED_LOCAL);
|
||||
return ERR_OK; /* lwiperf_tcp_close frees conn */
|
||||
}
|
||||
|
||||
if (!conn->base.server) {
|
||||
lwiperf_tcp_client_send_more(conn);
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/** This is called when a new client connects for an iperf tcp session */
|
||||
static err_t
|
||||
lwiperf_tcp_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
|
||||
{
|
||||
lwiperf_state_tcp_t *s, *conn;
|
||||
if ((err != ERR_OK) || (newpcb == NULL) || (arg == NULL)) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
s = (lwiperf_state_tcp_t*)arg;
|
||||
conn = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
|
||||
if (conn == NULL) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
memset(conn, 0, sizeof(lwiperf_state_tcp_t));
|
||||
conn->base.tcp = 1;
|
||||
conn->base.server = 1;
|
||||
conn->base.related_server_state = &s->base;
|
||||
conn->server_pcb = s->server_pcb;
|
||||
conn->conn_pcb = newpcb;
|
||||
conn->time_started = sys_now();
|
||||
conn->report_fn = s->report_fn;
|
||||
conn->report_arg = s->report_arg;
|
||||
|
||||
/* setup the tcp rx connection */
|
||||
tcp_arg(newpcb, conn);
|
||||
tcp_recv(newpcb, lwiperf_tcp_recv);
|
||||
tcp_poll(newpcb, lwiperf_tcp_poll, 2U);
|
||||
tcp_err(conn->conn_pcb, lwiperf_tcp_err);
|
||||
|
||||
lwiperf_list_add(&conn->base);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iperf
|
||||
* Start a TCP iperf server on the default TCP port (5001) and listen for
|
||||
* incoming connections from iperf clients.
|
||||
*
|
||||
* @returns a connection handle that can be used to abort the server
|
||||
* by calling @ref lwiperf_abort()
|
||||
*/
|
||||
void*
|
||||
lwiperf_start_tcp_server_default(lwiperf_report_fn report_fn, void* report_arg)
|
||||
{
|
||||
return lwiperf_start_tcp_server(IP4_ADDR_ANY, LWIPERF_TCP_PORT_DEFAULT,
|
||||
report_fn, report_arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iperf
|
||||
* Start a TCP iperf server on a specific IP address and port and listen for
|
||||
* incoming connections from iperf clients.
|
||||
*
|
||||
* @returns a connection handle that can be used to abort the server
|
||||
* by calling @ref lwiperf_abort()
|
||||
*/
|
||||
void*
|
||||
lwiperf_start_tcp_server(const ip_addr_t* local_addr, u16_t local_port,
|
||||
lwiperf_report_fn report_fn, void* report_arg)
|
||||
{
|
||||
err_t err;
|
||||
struct tcp_pcb* pcb;
|
||||
lwiperf_state_tcp_t* s;
|
||||
|
||||
if (local_addr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s = (lwiperf_state_tcp_t*)LWIPERF_ALLOC(lwiperf_state_tcp_t);
|
||||
if (s == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memset(s, 0, sizeof(lwiperf_state_tcp_t));
|
||||
s->base.tcp = 1;
|
||||
s->base.server = 1;
|
||||
s->report_fn = report_fn;
|
||||
s->report_arg = report_arg;
|
||||
|
||||
pcb = tcp_new();
|
||||
if (pcb != NULL) {
|
||||
err = tcp_bind(pcb, local_addr, local_port);
|
||||
if (err == ERR_OK) {
|
||||
s->server_pcb = tcp_listen_with_backlog(pcb, 1);
|
||||
}
|
||||
}
|
||||
if (s->server_pcb == NULL) {
|
||||
if (pcb != NULL) {
|
||||
tcp_close(pcb);
|
||||
}
|
||||
LWIPERF_FREE(lwiperf_state_tcp_t, s);
|
||||
return NULL;
|
||||
}
|
||||
pcb = NULL;
|
||||
|
||||
tcp_arg(s->server_pcb, s);
|
||||
tcp_accept(s->server_pcb, lwiperf_tcp_accept);
|
||||
|
||||
lwiperf_list_add(&s->base);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iperf
|
||||
* Abort an iperf session (handle returned by lwiperf_start_tcp_server*())
|
||||
*/
|
||||
void
|
||||
lwiperf_abort(void* lwiperf_session)
|
||||
{
|
||||
lwiperf_state_base_t* i, *dealloc, *last = NULL;
|
||||
|
||||
for (i = lwiperf_all_connections; i != NULL; ) {
|
||||
if ((i == lwiperf_session) || (i->related_server_state == lwiperf_session)) {
|
||||
dealloc = i;
|
||||
i = i->next;
|
||||
if (last != NULL) {
|
||||
last->next = i;
|
||||
}
|
||||
LWIPERF_FREE(lwiperf_state_tcp_t, dealloc); /* @todo: type? */
|
||||
} else {
|
||||
last = i;
|
||||
i = i->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_TCP */
|
||||
2031
src/apps/mdns/mdns.c
Normal file
2031
src/apps/mdns/mdns.c
Normal file
File diff suppressed because it is too large
Load Diff
367
src/apps/netbiosns/netbiosns.c
Normal file
367
src/apps/netbiosns/netbiosns.c
Normal file
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
* @file
|
||||
* NetBIOS name service responder
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup netbiosns NETBIOS responder
|
||||
* @ingroup apps
|
||||
*
|
||||
* This is an example implementation of a NetBIOS name server.
|
||||
* It responds to name queries for a configurable name.
|
||||
* Name resolving is not supported.
|
||||
*
|
||||
* Note that the device doesn't broadcast it's own name so can't
|
||||
* detect duplicate names!
|
||||
*/
|
||||
|
||||
/*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/apps/netbiosns.h"
|
||||
|
||||
#if LWIP_IPV4 && LWIP_UDP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/** default port number for "NetBIOS Name service */
|
||||
#define NETBIOS_PORT 137
|
||||
|
||||
/** size of a NetBIOS name */
|
||||
#define NETBIOS_NAME_LEN 16
|
||||
|
||||
/** The Time-To-Live for NetBIOS name responds (in seconds)
|
||||
* Default is 300000 seconds (3 days, 11 hours, 20 minutes) */
|
||||
#define NETBIOS_NAME_TTL 300000u
|
||||
|
||||
/** NetBIOS header flags */
|
||||
#define NETB_HFLAG_RESPONSE 0x8000U
|
||||
#define NETB_HFLAG_OPCODE 0x7800U
|
||||
#define NETB_HFLAG_OPCODE_NAME_QUERY 0x0000U
|
||||
#define NETB_HFLAG_AUTHORATIVE 0x0400U
|
||||
#define NETB_HFLAG_TRUNCATED 0x0200U
|
||||
#define NETB_HFLAG_RECURS_DESIRED 0x0100U
|
||||
#define NETB_HFLAG_RECURS_AVAILABLE 0x0080U
|
||||
#define NETB_HFLAG_BROADCAST 0x0010U
|
||||
#define NETB_HFLAG_REPLYCODE 0x0008U
|
||||
#define NETB_HFLAG_REPLYCODE_NOERROR 0x0000U
|
||||
|
||||
/** NetBIOS name flags */
|
||||
#define NETB_NFLAG_UNIQUE 0x8000U
|
||||
#define NETB_NFLAG_NODETYPE 0x6000U
|
||||
#define NETB_NFLAG_NODETYPE_HNODE 0x6000U
|
||||
#define NETB_NFLAG_NODETYPE_MNODE 0x4000U
|
||||
#define NETB_NFLAG_NODETYPE_PNODE 0x2000U
|
||||
#define NETB_NFLAG_NODETYPE_BNODE 0x0000U
|
||||
|
||||
/** NetBIOS message header */
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct netbios_hdr {
|
||||
PACK_STRUCT_FIELD(u16_t trans_id);
|
||||
PACK_STRUCT_FIELD(u16_t flags);
|
||||
PACK_STRUCT_FIELD(u16_t questions);
|
||||
PACK_STRUCT_FIELD(u16_t answerRRs);
|
||||
PACK_STRUCT_FIELD(u16_t authorityRRs);
|
||||
PACK_STRUCT_FIELD(u16_t additionalRRs);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
/** NetBIOS message name part */
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct netbios_name_hdr {
|
||||
PACK_STRUCT_FLD_8(u8_t nametype);
|
||||
PACK_STRUCT_FLD_8(u8_t encname[(NETBIOS_NAME_LEN*2)+1]);
|
||||
PACK_STRUCT_FIELD(u16_t type);
|
||||
PACK_STRUCT_FIELD(u16_t cls);
|
||||
PACK_STRUCT_FIELD(u32_t ttl);
|
||||
PACK_STRUCT_FIELD(u16_t datalen);
|
||||
PACK_STRUCT_FIELD(u16_t flags);
|
||||
PACK_STRUCT_FLD_S(ip4_addr_p_t addr);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
/** NetBIOS message */
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct netbios_resp
|
||||
{
|
||||
struct netbios_hdr resp_hdr;
|
||||
struct netbios_name_hdr resp_name;
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
#ifdef NETBIOS_LWIP_NAME
|
||||
#define NETBIOS_LOCAL_NAME NETBIOS_LWIP_NAME
|
||||
#else
|
||||
static char netbiosns_local_name[NETBIOS_NAME_LEN];
|
||||
#define NETBIOS_LOCAL_NAME netbiosns_local_name
|
||||
#endif
|
||||
|
||||
struct udp_pcb *netbiosns_pcb;
|
||||
|
||||
/** Decode a NetBIOS name (from packet to string) */
|
||||
static int
|
||||
netbiosns_name_decode(char *name_enc, char *name_dec, int name_dec_len)
|
||||
{
|
||||
char *pname;
|
||||
char cname;
|
||||
char cnbname;
|
||||
int idx = 0;
|
||||
|
||||
LWIP_UNUSED_ARG(name_dec_len);
|
||||
|
||||
/* Start decoding netbios name. */
|
||||
pname = name_enc;
|
||||
for (;;) {
|
||||
/* Every two characters of the first level-encoded name
|
||||
* turn into one character in the decoded name. */
|
||||
cname = *pname;
|
||||
if (cname == '\0')
|
||||
break; /* no more characters */
|
||||
if (cname == '.')
|
||||
break; /* scope ID follows */
|
||||
if (cname < 'A' || cname > 'Z') {
|
||||
/* Not legal. */
|
||||
return -1;
|
||||
}
|
||||
cname -= 'A';
|
||||
cnbname = cname << 4;
|
||||
pname++;
|
||||
|
||||
cname = *pname;
|
||||
if (cname == '\0' || cname == '.') {
|
||||
/* No more characters in the name - but we're in
|
||||
* the middle of a pair. Not legal. */
|
||||
return -1;
|
||||
}
|
||||
if (cname < 'A' || cname > 'Z') {
|
||||
/* Not legal. */
|
||||
return -1;
|
||||
}
|
||||
cname -= 'A';
|
||||
cnbname |= cname;
|
||||
pname++;
|
||||
|
||||
/* Do we have room to store the character? */
|
||||
if (idx < NETBIOS_NAME_LEN) {
|
||||
/* Yes - store the character. */
|
||||
name_dec[idx++] = (cnbname!=' '?cnbname:'\0');
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* function currently unused */
|
||||
/** Encode a NetBIOS name (from string to packet) - currently unused because
|
||||
we don't ask for names. */
|
||||
static int
|
||||
netbiosns_name_encode(char *name_enc, char *name_dec, int name_dec_len)
|
||||
{
|
||||
char *pname;
|
||||
char cname;
|
||||
unsigned char ucname;
|
||||
int idx = 0;
|
||||
|
||||
/* Start encoding netbios name. */
|
||||
pname = name_enc;
|
||||
|
||||
for (;;) {
|
||||
/* Every two characters of the first level-encoded name
|
||||
* turn into one character in the decoded name. */
|
||||
cname = *pname;
|
||||
if (cname == '\0')
|
||||
break; /* no more characters */
|
||||
if (cname == '.')
|
||||
break; /* scope ID follows */
|
||||
if ((cname < 'A' || cname > 'Z') && (cname < '0' || cname > '9')) {
|
||||
/* Not legal. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Do we have room to store the character? */
|
||||
if (idx >= name_dec_len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Yes - store the character. */
|
||||
ucname = cname;
|
||||
name_dec[idx++] = ('A'+((ucname>>4) & 0x0F));
|
||||
name_dec[idx++] = ('A'+( ucname & 0x0F));
|
||||
pname++;
|
||||
}
|
||||
|
||||
/* Fill with "space" coding */
|
||||
for (;idx < name_dec_len - 1;) {
|
||||
name_dec[idx++] = 'C';
|
||||
name_dec[idx++] = 'A';
|
||||
}
|
||||
|
||||
/* Terminate string */
|
||||
name_dec[idx] = '\0';
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* 0 */
|
||||
|
||||
/** NetBIOS Name service recv callback */
|
||||
static void
|
||||
netbiosns_recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
|
||||
{
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
|
||||
/* if packet is valid */
|
||||
if (p != NULL) {
|
||||
char netbios_name[NETBIOS_NAME_LEN+1];
|
||||
struct netbios_hdr* netbios_hdr = (struct netbios_hdr*)p->payload;
|
||||
struct netbios_name_hdr* netbios_name_hdr = (struct netbios_name_hdr*)(netbios_hdr+1);
|
||||
|
||||
/* we only answer if we got a default interface */
|
||||
if (netif_default != NULL) {
|
||||
/* @todo: do we need to check answerRRs/authorityRRs/additionalRRs? */
|
||||
/* if the packet is a NetBIOS name query question */
|
||||
if (((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_OPCODE)) == PP_NTOHS(NETB_HFLAG_OPCODE_NAME_QUERY)) &&
|
||||
((netbios_hdr->flags & PP_NTOHS(NETB_HFLAG_RESPONSE)) == 0) &&
|
||||
(netbios_hdr->questions == PP_NTOHS(1))) {
|
||||
/* decode the NetBIOS name */
|
||||
netbiosns_name_decode((char*)(netbios_name_hdr->encname), netbios_name, sizeof(netbios_name));
|
||||
/* if the packet is for us */
|
||||
if (lwip_strnicmp(netbios_name, NETBIOS_LOCAL_NAME, sizeof(NETBIOS_LOCAL_NAME)) == 0) {
|
||||
struct pbuf *q;
|
||||
struct netbios_resp *resp;
|
||||
|
||||
q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct netbios_resp), PBUF_RAM);
|
||||
if (q != NULL) {
|
||||
resp = (struct netbios_resp*)q->payload;
|
||||
|
||||
/* prepare NetBIOS header response */
|
||||
resp->resp_hdr.trans_id = netbios_hdr->trans_id;
|
||||
resp->resp_hdr.flags = PP_HTONS(NETB_HFLAG_RESPONSE |
|
||||
NETB_HFLAG_OPCODE_NAME_QUERY |
|
||||
NETB_HFLAG_AUTHORATIVE |
|
||||
NETB_HFLAG_RECURS_DESIRED);
|
||||
resp->resp_hdr.questions = 0;
|
||||
resp->resp_hdr.answerRRs = PP_HTONS(1);
|
||||
resp->resp_hdr.authorityRRs = 0;
|
||||
resp->resp_hdr.additionalRRs = 0;
|
||||
|
||||
/* prepare NetBIOS header datas */
|
||||
MEMCPY( resp->resp_name.encname, netbios_name_hdr->encname, sizeof(netbios_name_hdr->encname));
|
||||
resp->resp_name.nametype = netbios_name_hdr->nametype;
|
||||
resp->resp_name.type = netbios_name_hdr->type;
|
||||
resp->resp_name.cls = netbios_name_hdr->cls;
|
||||
resp->resp_name.ttl = PP_HTONL(NETBIOS_NAME_TTL);
|
||||
resp->resp_name.datalen = PP_HTONS(sizeof(resp->resp_name.flags)+sizeof(resp->resp_name.addr));
|
||||
resp->resp_name.flags = PP_HTONS(NETB_NFLAG_NODETYPE_BNODE);
|
||||
ip4_addr_copy(resp->resp_name.addr, *netif_ip4_addr(netif_default));
|
||||
|
||||
/* send the NetBIOS response */
|
||||
udp_sendto(upcb, q, addr, port);
|
||||
|
||||
/* free the "reference" pbuf */
|
||||
pbuf_free(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* free the pbuf */
|
||||
pbuf_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup netbiosns
|
||||
* Init netbios responder
|
||||
*/
|
||||
void
|
||||
netbiosns_init(void)
|
||||
{
|
||||
#ifdef NETBIOS_LWIP_NAME
|
||||
LWIP_ASSERT("NetBIOS name is too long!", strlen(NETBIOS_LWIP_NAME) < NETBIOS_NAME_LEN);
|
||||
#endif
|
||||
|
||||
netbiosns_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
|
||||
if (netbiosns_pcb != NULL) {
|
||||
/* we have to be allowed to send broadcast packets! */
|
||||
ip_set_option(netbiosns_pcb, SOF_BROADCAST);
|
||||
udp_bind(netbiosns_pcb, IP_ANY_TYPE, NETBIOS_PORT);
|
||||
udp_recv(netbiosns_pcb, netbiosns_recv, netbiosns_pcb);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NETBIOS_LWIP_NAME
|
||||
/**
|
||||
* @ingroup netbiosns
|
||||
* Set netbios name. ATTENTION: the hostname must be less than 15 characters!
|
||||
*/
|
||||
void
|
||||
netbiosns_set_name(const char* hostname)
|
||||
{
|
||||
size_t copy_len = strlen(hostname);
|
||||
LWIP_ASSERT("NetBIOS name is too long!", copy_len < NETBIOS_NAME_LEN);
|
||||
if (copy_len >= NETBIOS_NAME_LEN) {
|
||||
copy_len = NETBIOS_NAME_LEN - 1;
|
||||
}
|
||||
MEMCPY(netbiosns_local_name, hostname, copy_len + 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @ingroup netbiosns
|
||||
* Stop netbios responder
|
||||
*/
|
||||
void
|
||||
netbiosns_stop(void)
|
||||
{
|
||||
if (netbiosns_pcb != NULL) {
|
||||
udp_remove(netbiosns_pcb);
|
||||
netbiosns_pcb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_UDP */
|
||||
749
src/apps/snmp/snmp_asn1.c
Normal file
749
src/apps/snmp/snmp_asn1.c
Normal file
@@ -0,0 +1,749 @@
|
||||
/**
|
||||
* @file
|
||||
* Abstract Syntax Notation One (ISO 8824, 8825) encoding
|
||||
*
|
||||
* @todo not optimised (yet), favor correctness over speed, favor speed over size
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Christiaan Simons <christiaan.simons@axon.tv>
|
||||
* Martin Hentschel <info@cl-soft.de>
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "snmp_asn1.h"
|
||||
|
||||
#define PBUF_OP_EXEC(code) \
|
||||
if ((code) != ERR_OK) { \
|
||||
return ERR_BUF; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a TLV into a pbuf stream.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param tlv TLV to encode
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*/
|
||||
err_t
|
||||
snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
|
||||
{
|
||||
u8_t data;
|
||||
u8_t length_bytes_required;
|
||||
|
||||
/* write type */
|
||||
if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
|
||||
/* extended format is not used by SNMP so we do not accept those values */
|
||||
return ERR_ARG;
|
||||
}
|
||||
if (tlv->type_len != 0) {
|
||||
/* any other value as auto is not accepted for type (we always use one byte because extended syntax is prohibited) */
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, tlv->type));
|
||||
tlv->type_len = 1;
|
||||
|
||||
/* write length */
|
||||
if (tlv->value_len <= 127) {
|
||||
length_bytes_required = 1;
|
||||
} else if (tlv->value_len <= 255) {
|
||||
length_bytes_required = 2;
|
||||
} else {
|
||||
length_bytes_required = 3;
|
||||
}
|
||||
|
||||
/* check for forced min length */
|
||||
if (tlv->length_len > 0) {
|
||||
if (tlv->length_len < length_bytes_required) {
|
||||
/* unable to code requested length in requested number of bytes */
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
length_bytes_required = tlv->length_len;
|
||||
} else {
|
||||
tlv->length_len = length_bytes_required;
|
||||
}
|
||||
|
||||
if (length_bytes_required > 1) {
|
||||
/* multi byte representation required */
|
||||
length_bytes_required--;
|
||||
data = 0x80 | length_bytes_required; /* extended length definition, 1 length byte follows */
|
||||
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
|
||||
|
||||
while (length_bytes_required > 1) {
|
||||
if (length_bytes_required == 2) {
|
||||
/* append high byte */
|
||||
data = (u8_t)(tlv->value_len >> 8);
|
||||
} else {
|
||||
/* append leading 0x00 */
|
||||
data = 0x00;
|
||||
}
|
||||
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
|
||||
length_bytes_required--;
|
||||
}
|
||||
}
|
||||
|
||||
/* append low byte */
|
||||
data = (u8_t)(tlv->value_len & 0xFF);
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, data));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param raw_len raw data length
|
||||
* @param raw points raw data
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len)
|
||||
{
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_writebuf(pbuf_stream, raw, raw_len));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
|
||||
* @param value is the host order u32_t value to be encoded
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*
|
||||
* @see snmp_asn1_enc_u32t_cnt()
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value)
|
||||
{
|
||||
if (octets_needed > 5) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if (octets_needed == 5) {
|
||||
/* not enough bits in 'value' add leading 0x00 */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
|
||||
octets_needed--;
|
||||
}
|
||||
|
||||
while (octets_needed > 1) {
|
||||
octets_needed--;
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
|
||||
}
|
||||
|
||||
/* (only) one least significant octet */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes u64_t (counter64) into a pbuf chained ASN1 msg.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
|
||||
* @param value is the host order u32_t value to be encoded
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*
|
||||
* @see snmp_asn1_enc_u64t_cnt()
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value)
|
||||
{
|
||||
if (octets_needed > 9) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if (octets_needed == 9) {
|
||||
/* not enough bits in 'value' add leading 0x00 */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, 0x00));
|
||||
octets_needed--;
|
||||
}
|
||||
|
||||
while (octets_needed > 4) {
|
||||
octets_needed--;
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> ((octets_needed-4) << 3))));
|
||||
}
|
||||
|
||||
/* skip to low u32 */
|
||||
value++;
|
||||
|
||||
while (octets_needed > 1) {
|
||||
octets_needed--;
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value >> (octets_needed << 3))));
|
||||
}
|
||||
|
||||
/* always write at least one octet (also in case of value == 0) */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(*value)));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes s32_t integer into a pbuf chained ASN1 msg.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
|
||||
* @param value is the host order s32_t value to be encoded
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*
|
||||
* @see snmp_asn1_enc_s32t_cnt()
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value)
|
||||
{
|
||||
while (octets_needed > 1) {
|
||||
octets_needed--;
|
||||
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)(value >> (octets_needed << 3))));
|
||||
}
|
||||
|
||||
/* (only) one least significant octet */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)value));
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes object identifier into a pbuf chained ASN1 msg.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param oid points to object identifier array
|
||||
* @param oid_len object identifier array length
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) encode
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len)
|
||||
{
|
||||
if (oid_len > 1) {
|
||||
/* write compressed first two sub id's */
|
||||
u32_t compressed_byte = ((oid[0] * 40) + oid[1]);
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)compressed_byte));
|
||||
oid_len -= 2;
|
||||
oid += 2;
|
||||
} else {
|
||||
/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
|
||||
/* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
while (oid_len > 0) {
|
||||
u32_t sub_id;
|
||||
u8_t shift, tail;
|
||||
|
||||
oid_len--;
|
||||
sub_id = *oid;
|
||||
tail = 0;
|
||||
shift = 28;
|
||||
while (shift > 0) {
|
||||
u8_t code;
|
||||
|
||||
code = (u8_t)(sub_id >> shift);
|
||||
if ((code != 0) || (tail != 0)) {
|
||||
tail = 1;
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, code | 0x80));
|
||||
}
|
||||
shift -= 7;
|
||||
}
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_write(pbuf_stream, (u8_t)sub_id & 0x7F));
|
||||
|
||||
/* proceed to next sub-identifier */
|
||||
oid++;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns octet count for length.
|
||||
*
|
||||
* @param length parameter length
|
||||
* @param octets_needed points to the return value
|
||||
*/
|
||||
void
|
||||
snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
|
||||
{
|
||||
if (length < 0x80U) {
|
||||
*octets_needed = 1;
|
||||
} else if (length < 0x100U) {
|
||||
*octets_needed = 2;
|
||||
} else {
|
||||
*octets_needed = 3;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns octet count for an u32_t.
|
||||
*
|
||||
* @param value value to be encoded
|
||||
* @param octets_needed points to the return value
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
|
||||
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
|
||||
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
|
||||
*/
|
||||
void
|
||||
snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
|
||||
{
|
||||
if (value < 0x80UL) {
|
||||
*octets_needed = 1;
|
||||
} else if (value < 0x8000UL) {
|
||||
*octets_needed = 2;
|
||||
} else if (value < 0x800000UL) {
|
||||
*octets_needed = 3;
|
||||
} else if (value < 0x80000000UL) {
|
||||
*octets_needed = 4;
|
||||
} else {
|
||||
*octets_needed = 5;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns octet count for an u64_t.
|
||||
*
|
||||
* @param value value to be encoded
|
||||
* @param octets_needed points to the return value
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
|
||||
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
|
||||
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
|
||||
*/
|
||||
void
|
||||
snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed)
|
||||
{
|
||||
/* check if high u32 is 0 */
|
||||
if (*value == 0x00) {
|
||||
/* only low u32 is important */
|
||||
value++;
|
||||
snmp_asn1_enc_u32t_cnt(*value, octets_needed);
|
||||
} else {
|
||||
/* low u32 does not matter for length determination */
|
||||
snmp_asn1_enc_u32t_cnt(*value, octets_needed);
|
||||
*octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns octet count for an s32_t.
|
||||
*
|
||||
* @param value value to be encoded
|
||||
* @param octets_needed points to the return value
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed.
|
||||
*/
|
||||
void
|
||||
snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
|
||||
{
|
||||
if (value < 0) {
|
||||
value = ~value;
|
||||
}
|
||||
if (value < 0x80L) {
|
||||
*octets_needed = 1;
|
||||
} else if (value < 0x8000L) {
|
||||
*octets_needed = 2;
|
||||
} else if (value < 0x800000L) {
|
||||
*octets_needed = 3;
|
||||
} else {
|
||||
*octets_needed = 4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns octet count for an object identifier.
|
||||
*
|
||||
* @param oid points to object identifier array
|
||||
* @param oid_len object identifier array length
|
||||
* @param octets_needed points to the return value
|
||||
*/
|
||||
void
|
||||
snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed)
|
||||
{
|
||||
u32_t sub_id;
|
||||
|
||||
*octets_needed = 0;
|
||||
if (oid_len > 1) {
|
||||
/* compressed prefix in one octet */
|
||||
(*octets_needed)++;
|
||||
oid_len -= 2;
|
||||
oid += 2;
|
||||
}
|
||||
while (oid_len > 0) {
|
||||
oid_len--;
|
||||
sub_id = *oid;
|
||||
|
||||
sub_id >>= 7;
|
||||
(*octets_needed)++;
|
||||
while (sub_id > 0) {
|
||||
sub_id >>= 7;
|
||||
(*octets_needed)++;
|
||||
}
|
||||
oid++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a TLV from a pbuf stream.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param tlv returns decoded TLV
|
||||
* @return ERR_OK if successful, ERR_VAL if we can't decode
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv)
|
||||
{
|
||||
u8_t data;
|
||||
|
||||
/* decode type first */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
tlv->type = data;
|
||||
|
||||
if ((tlv->type & SNMP_ASN1_DATATYPE_MASK) == SNMP_ASN1_DATATYPE_EXTENDED) {
|
||||
/* extended format is not used by SNMP so we do not accept those values */
|
||||
return ERR_VAL;
|
||||
}
|
||||
tlv->type_len = 1;
|
||||
|
||||
/* now, decode length */
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
|
||||
if (data < 0x80) { /* short form */
|
||||
tlv->length_len = 1;
|
||||
tlv->value_len = data;
|
||||
} else if (data > 0x80) { /* long form */
|
||||
u8_t length_bytes = data - 0x80;
|
||||
tlv->length_len = length_bytes + 1; /* this byte + defined number of length bytes following */
|
||||
tlv->value_len = 0;
|
||||
|
||||
while (length_bytes > 0) {
|
||||
/* we only support up to u16.maxvalue-1 (2 bytes) but have to accept leading zero bytes */
|
||||
if (tlv->value_len > 0xFF) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
tlv->value_len <<= 8;
|
||||
tlv->value_len |= data;
|
||||
|
||||
/* take care for special value used for indefinite length */
|
||||
if (tlv->value_len == 0xFFFF) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
length_bytes--;
|
||||
}
|
||||
} else { /* data == 0x80 indefinite length form */
|
||||
/* (not allowed for SNMP; RFC 1157, 3.2.2) */
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes positive integer (counter, gauge, timeticks) into u32_t.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param len length of the coded integer field
|
||||
* @param value return host order integer
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
|
||||
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
|
||||
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
|
||||
{
|
||||
u8_t data;
|
||||
|
||||
if ((len > 0) && (len <= 5)) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
|
||||
/* expecting sign bit to be zero, only unsigned please! */
|
||||
if (((len == 5) && (data == 0x00)) || ((len < 5) && ((data & 0x80) == 0))) {
|
||||
*value = data;
|
||||
len--;
|
||||
|
||||
while (len > 0) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
*value <<= 8;
|
||||
*value |= data;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes large positive integer (counter64) into 2x u32_t.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param len length of the coded integer field
|
||||
* @param value return host order integer
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
|
||||
* as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
|
||||
* of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value)
|
||||
{
|
||||
u8_t data;
|
||||
|
||||
if (len <= 4) {
|
||||
/* high u32 is 0 */
|
||||
*value = 0;
|
||||
/* directly skip to low u32 */
|
||||
value++;
|
||||
}
|
||||
|
||||
if ((len > 0) && (len <= 9)) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
|
||||
/* expecting sign bit to be zero, only unsigned please! */
|
||||
if (((len == 9) && (data == 0x00)) || ((len < 9) && ((data & 0x80) == 0))) {
|
||||
*value = data;
|
||||
len--;
|
||||
|
||||
while (len > 0) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
|
||||
if (len == 4) {
|
||||
/* skip to low u32 */
|
||||
value++;
|
||||
*value = 0;
|
||||
} else {
|
||||
*value <<= 8;
|
||||
}
|
||||
|
||||
*value |= data;
|
||||
len--;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes integer into s32_t.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param len length of the coded integer field
|
||||
* @param value return host order integer
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
|
||||
*
|
||||
* @note ASN coded integers are _always_ signed!
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value)
|
||||
{
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
u8_t *lsb_ptr = (u8_t*)value;
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
|
||||
#endif
|
||||
u8_t sign;
|
||||
u8_t data;
|
||||
|
||||
if ((len > 0) && (len < 5)) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
if (data & 0x80) {
|
||||
/* negative, start from -1 */
|
||||
*value = -1;
|
||||
sign = 1;
|
||||
*lsb_ptr &= data;
|
||||
} else {
|
||||
/* positive, start from 0 */
|
||||
*value = 0;
|
||||
sign = 0;
|
||||
*lsb_ptr |= data;
|
||||
}
|
||||
|
||||
/* OR/AND octets with value */
|
||||
while (len > 0) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
*value <<= 8;
|
||||
#endif
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
*value >>= 8;
|
||||
#endif
|
||||
|
||||
if (sign) {
|
||||
*lsb_ptr |= 255;
|
||||
*lsb_ptr &= data;
|
||||
} else {
|
||||
*lsb_ptr |= data;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes object identifier from incoming message into array of u32_t.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param len length of the coded object identifier
|
||||
* @param oid return decoded object identifier
|
||||
* @param oid_len return decoded object identifier length
|
||||
* @param oid_max_len size of oid buffer
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len)
|
||||
{
|
||||
u32_t *oid_ptr;
|
||||
u8_t data;
|
||||
|
||||
*oid_len = 0;
|
||||
oid_ptr = oid;
|
||||
if (len > 0) {
|
||||
if (oid_max_len < 2) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
/* first compressed octet */
|
||||
if (data == 0x2B) {
|
||||
/* (most) common case 1.3 (iso.org) */
|
||||
*oid_ptr = 1;
|
||||
oid_ptr++;
|
||||
*oid_ptr = 3;
|
||||
oid_ptr++;
|
||||
} else if (data < 40) {
|
||||
*oid_ptr = 0;
|
||||
oid_ptr++;
|
||||
*oid_ptr = data;
|
||||
oid_ptr++;
|
||||
} else if (data < 80) {
|
||||
*oid_ptr = 1;
|
||||
oid_ptr++;
|
||||
*oid_ptr = data - 40;
|
||||
oid_ptr++;
|
||||
} else {
|
||||
*oid_ptr = 2;
|
||||
oid_ptr++;
|
||||
*oid_ptr = data - 80;
|
||||
oid_ptr++;
|
||||
}
|
||||
*oid_len = 2;
|
||||
} else {
|
||||
/* accepting zero length identifiers e.g. for getnext operation. uncommon but valid */
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
while ((len > 0) && (*oid_len < oid_max_len)) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
if ((data & 0x80) == 0x00) {
|
||||
/* sub-identifier uses single octet */
|
||||
*oid_ptr = data;
|
||||
} else {
|
||||
/* sub-identifier uses multiple octets */
|
||||
u32_t sub_id = (data & ~0x80);
|
||||
while ((len > 0) && ((data & 0x80) != 0)) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
|
||||
len--;
|
||||
|
||||
sub_id = (sub_id << 7) + (data & ~0x80);
|
||||
}
|
||||
|
||||
if ((data & 0x80) != 0) {
|
||||
/* "more bytes following" bit still set at end of len */
|
||||
return ERR_VAL;
|
||||
}
|
||||
*oid_ptr = sub_id;
|
||||
}
|
||||
oid_ptr++;
|
||||
(*oid_len)++;
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
/* OID to long to fit in our buffer */
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
|
||||
* from incoming message into array.
|
||||
*
|
||||
* @param pbuf_stream points to a pbuf stream
|
||||
* @param len length of the coded raw data (zero is valid, e.g. empty string!)
|
||||
* @param buf return raw bytes
|
||||
* @param buf_len returns length of the raw return value
|
||||
* @param buf_max_len buffer size
|
||||
* @return ERR_OK if successful, ERR_ARG if we can't (or won't) decode
|
||||
*/
|
||||
err_t
|
||||
snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len)
|
||||
{
|
||||
if (len > buf_max_len) {
|
||||
/* not enough dst space */
|
||||
return ERR_MEM;
|
||||
}
|
||||
*buf_len = len;
|
||||
|
||||
while (len > 0) {
|
||||
PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, buf));
|
||||
buf++;
|
||||
len--;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
108
src/apps/snmp/snmp_asn1.h
Normal file
108
src/apps/snmp/snmp_asn1.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* @file
|
||||
* Abstract Syntax Notation One (ISO 8824, 8825) codec.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* Copyright (c) 2016 Elias Oenal.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Christiaan Simons <christiaan.simons@axon.tv>
|
||||
* Martin Hentschel <info@cl-soft.de>
|
||||
* Elias Oenal <lwip@eliasoenal.com>
|
||||
*/
|
||||
|
||||
#ifndef LWIP_HDR_APPS_SNMP_ASN1_H
|
||||
#define LWIP_HDR_APPS_SNMP_ASN1_H
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "snmp_pbuf_stream.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SNMP_ASN1_TLV_INDEFINITE_LENGTH 0x80
|
||||
|
||||
#define SNMP_ASN1_CLASS_MASK 0xC0
|
||||
#define SNMP_ASN1_CONTENTTYPE_MASK 0x20
|
||||
#define SNMP_ASN1_DATATYPE_MASK 0x1F
|
||||
#define SNMP_ASN1_DATATYPE_EXTENDED 0x1F /* DataType indicating that datatype is encoded in following bytes */
|
||||
|
||||
/* context specific (SNMP) tags (from SNMP spec. RFC1157) */
|
||||
#define SNMP_ASN1_CONTEXT_PDU_GET_REQ 0
|
||||
#define SNMP_ASN1_CONTEXT_PDU_GET_NEXT_REQ 1
|
||||
#define SNMP_ASN1_CONTEXT_PDU_GET_RESP 2
|
||||
#define SNMP_ASN1_CONTEXT_PDU_SET_REQ 3
|
||||
#define SNMP_ASN1_CONTEXT_PDU_TRAP 4
|
||||
#define SNMP_ASN1_CONTEXT_PDU_GET_BULK_REQ 5
|
||||
|
||||
#define SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT 0
|
||||
#define SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW 2
|
||||
|
||||
struct snmp_asn1_tlv
|
||||
{
|
||||
u8_t type; /* only U8 because extended types are not specified by SNMP */
|
||||
u8_t type_len; /* encoded length of 'type' field (normally 1) */
|
||||
u8_t length_len; /* indicates how many bytes are required to encode the 'value_len' field */
|
||||
u16_t value_len; /* encoded length of the value */
|
||||
};
|
||||
#define SNMP_ASN1_TLV_HDR_LENGTH(tlv) ((tlv).type_len + (tlv).length_len)
|
||||
#define SNMP_ASN1_TLV_LENGTH(tlv) ((tlv).type_len + (tlv).length_len + (tlv).value_len)
|
||||
#define SNMP_ASN1_SET_TLV_PARAMS(tlv, type_, length_len_, value_len_) do { (tlv).type = (type_); (tlv).type_len = 0; (tlv).length_len = (length_len_); (tlv).value_len = (value_len_); } while (0);
|
||||
|
||||
err_t snmp_asn1_dec_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv);
|
||||
err_t snmp_asn1_dec_u32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
|
||||
err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t *value);
|
||||
err_t snmp_asn1_dec_s32t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, s32_t *value);
|
||||
err_t snmp_asn1_dec_oid(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u32_t* oid, u8_t* oid_len, u8_t oid_max_len);
|
||||
err_t snmp_asn1_dec_raw(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u8_t *buf, u16_t* buf_len, u16_t buf_max_len);
|
||||
|
||||
err_t snmp_ans1_enc_tlv(struct snmp_pbuf_stream* pbuf_stream, struct snmp_asn1_tlv* tlv);
|
||||
|
||||
void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
|
||||
void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
|
||||
void snmp_asn1_enc_u64t_cnt(const u32_t *value, u16_t *octets_needed);
|
||||
void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
|
||||
void snmp_asn1_enc_oid_cnt(const u32_t *oid, u16_t oid_len, u16_t *octets_needed);
|
||||
err_t snmp_asn1_enc_oid(struct snmp_pbuf_stream* pbuf_stream, const u32_t *oid, u16_t oid_len);
|
||||
err_t snmp_asn1_enc_s32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, s32_t value);
|
||||
err_t snmp_asn1_enc_u32t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, u32_t value);
|
||||
err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream* pbuf_stream, u16_t octets_needed, const u32_t* value);
|
||||
err_t snmp_asn1_enc_raw(struct snmp_pbuf_stream* pbuf_stream, const u8_t *raw, u16_t raw_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
|
||||
#endif /* LWIP_HDR_APPS_SNMP_ASN1_H */
|
||||
1349
src/apps/snmp/snmp_core.c
Normal file
1349
src/apps/snmp/snmp_core.c
Normal file
File diff suppressed because it is too large
Load Diff
76
src/apps/snmp/snmp_core_priv.h
Normal file
76
src/apps/snmp/snmp_core_priv.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Martin Hentschel <info@cl-soft.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LWIP_HDR_APPS_SNMP_CORE_PRIV_H
|
||||
#define LWIP_HDR_APPS_SNMP_CORE_PRIV_H
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "snmp_asn1.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* (outdated) SNMPv1 error codes
|
||||
* shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
|
||||
*/
|
||||
#define SNMP_ERR_NOSUCHNAME 2
|
||||
#define SNMP_ERR_BADVALUE 3
|
||||
#define SNMP_ERR_READONLY 4
|
||||
/* error codes which are internal and shall not be used by MIBS
|
||||
* shall not be used by MIBS anymore, nevertheless required from core for properly answering a v1 request
|
||||
*/
|
||||
#define SNMP_ERR_TOOBIG 1
|
||||
#define SNMP_ERR_AUTHORIZATIONERROR 16
|
||||
#define SNMP_ERR_NOSUCHOBJECT SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_NO_SUCH_OBJECT
|
||||
#define SNMP_ERR_ENDOFMIBVIEW SNMP_VARBIND_EXCEPTION_OFFSET + SNMP_ASN1_CONTEXT_VARBIND_END_OF_MIB_VIEW
|
||||
|
||||
|
||||
const struct snmp_node* snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t* oid_instance_len);
|
||||
const struct snmp_node* snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id* oidret);
|
||||
|
||||
typedef u8_t (*snmp_validate_node_instance_method)(struct snmp_node_instance*, void*);
|
||||
|
||||
u8_t snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance* node_instance);
|
||||
u8_t snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void* validate_node_instance_arg, struct snmp_obj_id* node_oid, struct snmp_node_instance* node_instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
|
||||
#endif /* LWIP_HDR_APPS_SNMP_CORE_PRIV_H */
|
||||
116
src/apps/snmp/snmp_mib2.c
Normal file
116
src/apps/snmp/snmp_mib2.c
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup snmp_mib2 MIB2
|
||||
* @ingroup snmp
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2 /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#if !LWIP_STATS
|
||||
#error LWIP_SNMP MIB2 needs LWIP_STATS (for MIB2)
|
||||
#endif
|
||||
#if !MIB2_STATS
|
||||
#error LWIP_SNMP MIB2 needs MIB2_STATS (for MIB2)
|
||||
#endif
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/priv/tcpip_priv.h"
|
||||
void
|
||||
snmp_mib2_lwip_synchronizer(snmp_threadsync_called_fn fn, void* arg)
|
||||
{
|
||||
#if LWIP_TCPIP_CORE_LOCKING
|
||||
LOCK_TCPIP_CORE();
|
||||
fn(arg);
|
||||
UNLOCK_TCPIP_CORE();
|
||||
#else
|
||||
tcpip_callback(fn, arg);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct snmp_threadsync_instance snmp_mib2_lwip_locks;
|
||||
#endif
|
||||
|
||||
/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */
|
||||
/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */
|
||||
/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */
|
||||
|
||||
/* --- mib-2 .1.3.6.1.2.1 ----------------------------------------------------- */
|
||||
extern const struct snmp_scalar_array_node snmp_mib2_snmp_root;
|
||||
extern const struct snmp_tree_node snmp_mib2_udp_root;
|
||||
extern const struct snmp_tree_node snmp_mib2_tcp_root;
|
||||
extern const struct snmp_scalar_array_node snmp_mib2_icmp_root;
|
||||
extern const struct snmp_tree_node snmp_mib2_interface_root;
|
||||
extern const struct snmp_scalar_array_node snmp_mib2_system_node;
|
||||
extern const struct snmp_tree_node snmp_mib2_at_root;
|
||||
extern const struct snmp_tree_node snmp_mib2_ip_root;
|
||||
|
||||
static const struct snmp_node* const mib2_nodes[] = {
|
||||
&snmp_mib2_system_node.node.node,
|
||||
&snmp_mib2_interface_root.node,
|
||||
#if LWIP_ARP && LWIP_IPV4
|
||||
&snmp_mib2_at_root.node,
|
||||
#endif /* LWIP_ARP && LWIP_IPV4 */
|
||||
#if LWIP_IPV4
|
||||
&snmp_mib2_ip_root.node,
|
||||
#endif /* LWIP_IPV4 */
|
||||
#if LWIP_ICMP
|
||||
&snmp_mib2_icmp_root.node.node,
|
||||
#endif /* LWIP_ICMP */
|
||||
#if LWIP_TCP
|
||||
&snmp_mib2_tcp_root.node,
|
||||
#endif /* LWIP_TCP */
|
||||
#if LWIP_UDP
|
||||
&snmp_mib2_udp_root.node,
|
||||
#endif /* LWIP_UDP */
|
||||
&snmp_mib2_snmp_root.node.node
|
||||
};
|
||||
|
||||
static const struct snmp_tree_node mib2_root = SNMP_CREATE_TREE_NODE(1, mib2_nodes);
|
||||
|
||||
static const u32_t mib2_base_oid_arr[] = { 1,3,6,1,2,1 };
|
||||
const struct snmp_mib mib2 = SNMP_MIB_CREATE(mib2_base_oid_arr, &mib2_root.node);
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
|
||||
182
src/apps/snmp/snmp_mib2_icmp.c
Normal file
182
src/apps/snmp/snmp_mib2_icmp.c
Normal file
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) ICMP objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/icmp.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#define SYNC_NODE_NAME(node_name) node_name ## _synced
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
|
||||
static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
|
||||
#else
|
||||
#define SYNC_NODE_NAME(node_name) node_name
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name)
|
||||
#endif
|
||||
|
||||
/* --- icmp .1.3.6.1.2.1.5 ----------------------------------------------------- */
|
||||
|
||||
static s16_t
|
||||
icmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
|
||||
{
|
||||
u32_t *uint_ptr = (u32_t*)value;
|
||||
|
||||
switch (node->oid) {
|
||||
case 1: /* icmpInMsgs */
|
||||
*uint_ptr = STATS_GET(mib2.icmpinmsgs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 2: /* icmpInErrors */
|
||||
*uint_ptr = STATS_GET(mib2.icmpinerrors);
|
||||
return sizeof(*uint_ptr);
|
||||
case 3: /* icmpInDestUnreachs */
|
||||
*uint_ptr = STATS_GET(mib2.icmpindestunreachs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 4: /* icmpInTimeExcds */
|
||||
*uint_ptr = STATS_GET(mib2.icmpintimeexcds);
|
||||
return sizeof(*uint_ptr);
|
||||
case 5: /* icmpInParmProbs */
|
||||
*uint_ptr = STATS_GET(mib2.icmpinparmprobs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 6: /* icmpInSrcQuenchs */
|
||||
*uint_ptr = STATS_GET(mib2.icmpinsrcquenchs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 7: /* icmpInRedirects */
|
||||
*uint_ptr = STATS_GET(mib2.icmpinredirects);
|
||||
return sizeof(*uint_ptr);
|
||||
case 8: /* icmpInEchos */
|
||||
*uint_ptr = STATS_GET(mib2.icmpinechos);
|
||||
return sizeof(*uint_ptr);
|
||||
case 9: /* icmpInEchoReps */
|
||||
*uint_ptr = STATS_GET(mib2.icmpinechoreps);
|
||||
return sizeof(*uint_ptr);
|
||||
case 10: /* icmpInTimestamps */
|
||||
*uint_ptr = STATS_GET(mib2.icmpintimestamps);
|
||||
return sizeof(*uint_ptr);
|
||||
case 11: /* icmpInTimestampReps */
|
||||
*uint_ptr = STATS_GET(mib2.icmpintimestampreps);
|
||||
return sizeof(*uint_ptr);
|
||||
case 12: /* icmpInAddrMasks */
|
||||
*uint_ptr = STATS_GET(mib2.icmpinaddrmasks);
|
||||
return sizeof(*uint_ptr);
|
||||
case 13: /* icmpInAddrMaskReps */
|
||||
*uint_ptr = STATS_GET(mib2.icmpinaddrmaskreps);
|
||||
return sizeof(*uint_ptr);
|
||||
case 14: /* icmpOutMsgs */
|
||||
*uint_ptr = STATS_GET(mib2.icmpoutmsgs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 15: /* icmpOutErrors */
|
||||
*uint_ptr = STATS_GET(mib2.icmpouterrors);
|
||||
return sizeof(*uint_ptr);
|
||||
case 16: /* icmpOutDestUnreachs */
|
||||
*uint_ptr = STATS_GET(mib2.icmpoutdestunreachs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 17: /* icmpOutTimeExcds */
|
||||
*uint_ptr = STATS_GET(mib2.icmpouttimeexcds);
|
||||
return sizeof(*uint_ptr);
|
||||
case 18: /* icmpOutParmProbs: not supported -> always 0 */
|
||||
*uint_ptr = 0;
|
||||
return sizeof(*uint_ptr);
|
||||
case 19: /* icmpOutSrcQuenchs: not supported -> always 0 */
|
||||
*uint_ptr = 0;
|
||||
return sizeof(*uint_ptr);
|
||||
case 20: /* icmpOutRedirects: not supported -> always 0 */
|
||||
*uint_ptr = 0;
|
||||
return sizeof(*uint_ptr);
|
||||
case 21: /* icmpOutEchos */
|
||||
*uint_ptr = STATS_GET(mib2.icmpoutechos);
|
||||
return sizeof(*uint_ptr);
|
||||
case 22: /* icmpOutEchoReps */
|
||||
*uint_ptr = STATS_GET(mib2.icmpoutechoreps);
|
||||
return sizeof(*uint_ptr);
|
||||
case 23: /* icmpOutTimestamps: not supported -> always 0 */
|
||||
*uint_ptr = 0;
|
||||
return sizeof(*uint_ptr);
|
||||
case 24: /* icmpOutTimestampReps: not supported -> always 0 */
|
||||
*uint_ptr = 0;
|
||||
return sizeof(*uint_ptr);
|
||||
case 25: /* icmpOutAddrMasks: not supported -> always 0 */
|
||||
*uint_ptr = 0;
|
||||
return sizeof(*uint_ptr);
|
||||
case 26: /* icmpOutAddrMaskReps: not supported -> always 0 */
|
||||
*uint_ptr = 0;
|
||||
return sizeof(*uint_ptr);
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_value(): unknown id: %"S32_F"\n", node->oid));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct snmp_scalar_array_node_def icmp_nodes[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{ 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{ 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{ 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{ 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{ 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{ 7, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{ 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{ 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{23, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY},
|
||||
{26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}
|
||||
};
|
||||
|
||||
const struct snmp_scalar_array_node snmp_mib2_icmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(5, icmp_nodes, icmp_get_value, NULL, NULL);
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_ICMP */
|
||||
375
src/apps/snmp/snmp_mib2_interfaces.c
Normal file
375
src/apps/snmp/snmp_mib2_interfaces.c
Normal file
@@ -0,0 +1,375 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) INTERFACES objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#define SYNC_NODE_NAME(node_name) node_name ## _synced
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
|
||||
static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
|
||||
#else
|
||||
#define SYNC_NODE_NAME(node_name) node_name
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name)
|
||||
#endif
|
||||
|
||||
|
||||
/* --- interfaces .1.3.6.1.2.1.2 ----------------------------------------------------- */
|
||||
|
||||
static s16_t
|
||||
interfaces_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
if (instance->node->oid == 1) {
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
s32_t num_netifs = 0;
|
||||
|
||||
struct netif *netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
num_netifs++;
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
*sint_ptr = num_netifs;
|
||||
return sizeof(*sint_ptr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range interfaces_Table_oid_ranges[] = {
|
||||
{ 1, 0xff } /* netif->num is u8_t */
|
||||
};
|
||||
|
||||
static const u8_t iftable_ifOutQLen = 0;
|
||||
|
||||
static const u8_t iftable_ifOperStatus_up = 1;
|
||||
static const u8_t iftable_ifOperStatus_down = 2;
|
||||
|
||||
static const u8_t iftable_ifAdminStatus_up = 1;
|
||||
static const u8_t iftable_ifAdminStatus_lowerLayerDown = 7;
|
||||
static const u8_t iftable_ifAdminStatus_down = 2;
|
||||
|
||||
static snmp_err_t
|
||||
interfaces_Table_get_cell_instance(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, struct snmp_node_instance* cell_instance)
|
||||
{
|
||||
u32_t ifIndex;
|
||||
struct netif *netif;
|
||||
|
||||
LWIP_UNUSED_ARG(column);
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, interfaces_Table_oid_ranges, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get netif index from incoming OID */
|
||||
ifIndex = row_oid[0];
|
||||
|
||||
/* find netif with index */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
if (netif_to_num(netif) == ifIndex) {
|
||||
/* store netif pointer for subsequent operations (get/test/set) */
|
||||
cell_instance->reference.ptr = netif;
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
interfaces_Table_get_next_cell_instance(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance)
|
||||
{
|
||||
struct netif *netif;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
|
||||
|
||||
LWIP_UNUSED_ARG(column);
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
|
||||
test_oid[0] = netif_to_num(netif);
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif);
|
||||
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* store netif pointer for subsequent operations (get/test/set) */
|
||||
cell_instance->reference.ptr = /* (struct netif*) */state.reference;
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static s16_t
|
||||
interfaces_Table_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
struct netif *netif = (struct netif*)instance->reference.ptr;
|
||||
u32_t* value_u32 = (u32_t*)value;
|
||||
s32_t* value_s32 = (s32_t*)value;
|
||||
u16_t value_len;
|
||||
|
||||
switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id))
|
||||
{
|
||||
case 1: /* ifIndex */
|
||||
*value_s32 = netif_to_num(netif);
|
||||
value_len = sizeof(*value_s32);
|
||||
break;
|
||||
case 2: /* ifDescr */
|
||||
value_len = sizeof(netif->name);
|
||||
MEMCPY(value, netif->name, value_len);
|
||||
break;
|
||||
case 3: /* ifType */
|
||||
*value_s32 = netif->link_type;
|
||||
value_len = sizeof(*value_s32);
|
||||
break;
|
||||
case 4: /* ifMtu */
|
||||
*value_s32 = netif->mtu;
|
||||
value_len = sizeof(*value_s32);
|
||||
break;
|
||||
case 5: /* ifSpeed */
|
||||
*value_u32 = netif->link_speed;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 6: /* ifPhysAddress */
|
||||
value_len = sizeof(netif->hwaddr);
|
||||
MEMCPY(value, &netif->hwaddr, value_len);
|
||||
break;
|
||||
case 7: /* ifAdminStatus */
|
||||
if (netif_is_up(netif)) {
|
||||
*value_s32 = iftable_ifOperStatus_up;
|
||||
} else {
|
||||
*value_s32 = iftable_ifOperStatus_down;
|
||||
}
|
||||
value_len = sizeof(*value_s32);
|
||||
break;
|
||||
case 8: /* ifOperStatus */
|
||||
if (netif_is_up(netif)) {
|
||||
if (netif_is_link_up(netif)) {
|
||||
*value_s32 = iftable_ifAdminStatus_up;
|
||||
} else {
|
||||
*value_s32 = iftable_ifAdminStatus_lowerLayerDown;
|
||||
}
|
||||
} else {
|
||||
*value_s32 = iftable_ifAdminStatus_down;
|
||||
}
|
||||
value_len = sizeof(*value_s32);
|
||||
break;
|
||||
case 9: /* ifLastChange */
|
||||
*value_u32 = netif->ts;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 10: /* ifInOctets */
|
||||
*value_u32 = netif->mib2_counters.ifinoctets;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 11: /* ifInUcastPkts */
|
||||
*value_u32 = netif->mib2_counters.ifinucastpkts;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 12: /* ifInNUcastPkts */
|
||||
*value_u32 = netif->mib2_counters.ifinnucastpkts;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 13: /* ifInDiscards */
|
||||
*value_u32 = netif->mib2_counters.ifindiscards;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 14: /* ifInErrors */
|
||||
*value_u32 = netif->mib2_counters.ifinerrors;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 15: /* ifInUnkownProtos */
|
||||
*value_u32 = netif->mib2_counters.ifinunknownprotos;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 16: /* ifOutOctets */
|
||||
*value_u32 = netif->mib2_counters.ifoutoctets;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 17: /* ifOutUcastPkts */
|
||||
*value_u32 = netif->mib2_counters.ifoutucastpkts;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 18: /* ifOutNUcastPkts */
|
||||
*value_u32 = netif->mib2_counters.ifoutnucastpkts;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 19: /* ifOutDiscarts */
|
||||
*value_u32 = netif->mib2_counters.ifoutdiscards;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 20: /* ifOutErrors */
|
||||
*value_u32 = netif->mib2_counters.ifouterrors;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
case 21: /* ifOutQLen */
|
||||
*value_u32 = iftable_ifOutQLen;
|
||||
value_len = sizeof(*value_u32);
|
||||
break;
|
||||
/** @note returning zeroDotZero (0.0) no media specific MIB support */
|
||||
case 22: /* ifSpecific */
|
||||
value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
|
||||
MEMCPY(value, snmp_zero_dot_zero.id, value_len);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value_len;
|
||||
}
|
||||
|
||||
#if !SNMP_SAFE_REQUESTS
|
||||
|
||||
static snmp_err_t
|
||||
interfaces_Table_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
|
||||
{
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
|
||||
/* stack should never call this method for another column,
|
||||
because all other columns are set to readonly */
|
||||
LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
|
||||
LWIP_UNUSED_ARG(len);
|
||||
|
||||
if (*sint_ptr == 1 || *sint_ptr == 2) {
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
return SNMP_ERR_WRONGVALUE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
interfaces_Table_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
|
||||
{
|
||||
struct netif *netif = (struct netif*)instance->reference.ptr;
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
|
||||
/* stack should never call this method for another column,
|
||||
because all other columns are set to readonly */
|
||||
LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
|
||||
LWIP_UNUSED_ARG(len);
|
||||
|
||||
if (*sint_ptr == 1) {
|
||||
netif_set_up(netif);
|
||||
} else if (*sint_ptr == 2) {
|
||||
netif_set_down(netif);
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
#endif /* SNMP_SAFE_REQUESTS */
|
||||
|
||||
static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value);
|
||||
|
||||
static const struct snmp_table_col_def interfaces_Table_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */
|
||||
{ 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */
|
||||
{ 3, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */
|
||||
{ 4, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */
|
||||
{ 5, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */
|
||||
{ 6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */
|
||||
#if !SNMP_SAFE_REQUESTS
|
||||
{ 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */
|
||||
#else
|
||||
{ 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */
|
||||
#endif
|
||||
{ 8, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */
|
||||
{ 9, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */
|
||||
{ 10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */
|
||||
{ 11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */
|
||||
{ 12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */
|
||||
{ 13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */
|
||||
{ 14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */
|
||||
{ 15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */
|
||||
{ 16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */
|
||||
{ 17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */
|
||||
{ 18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */
|
||||
{ 19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */
|
||||
{ 20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */
|
||||
{ 21, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */
|
||||
{ 22, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY } /* ifSpecific */
|
||||
};
|
||||
|
||||
#if !SNMP_SAFE_REQUESTS
|
||||
static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
|
||||
2, interfaces_Table_columns,
|
||||
interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
|
||||
interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value);
|
||||
#else
|
||||
static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
|
||||
2, interfaces_Table_columns,
|
||||
interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
|
||||
interfaces_Table_get_value, NULL, NULL);
|
||||
#endif
|
||||
|
||||
/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
|
||||
CREATE_LWIP_SYNC_NODE(1, interfaces_Number)
|
||||
CREATE_LWIP_SYNC_NODE(2, interfaces_Table)
|
||||
|
||||
static const struct snmp_node* const interface_nodes[] = {
|
||||
&SYNC_NODE_NAME(interfaces_Number).node.node,
|
||||
&SYNC_NODE_NAME(interfaces_Table).node.node
|
||||
};
|
||||
|
||||
const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes);
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
|
||||
743
src/apps/snmp/snmp_mib2_ip.c
Normal file
743
src/apps/snmp/snmp_mib2_ip.c
Normal file
@@ -0,0 +1,743 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) IP objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/etharp.h"
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#define SYNC_NODE_NAME(node_name) node_name ## _synced
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
|
||||
static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
|
||||
#else
|
||||
#define SYNC_NODE_NAME(node_name) node_name
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name)
|
||||
#endif
|
||||
|
||||
#if LWIP_IPV4
|
||||
/* --- ip .1.3.6.1.2.1.4 ----------------------------------------------------- */
|
||||
|
||||
static s16_t
|
||||
ip_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
s32_t* sint_ptr = (s32_t*)value;
|
||||
u32_t* uint_ptr = (u32_t*)value;
|
||||
|
||||
switch (instance->node->oid) {
|
||||
case 1: /* ipForwarding */
|
||||
#if IP_FORWARD
|
||||
/* forwarding */
|
||||
*sint_ptr = 1;
|
||||
#else
|
||||
/* not-forwarding */
|
||||
*sint_ptr = 2;
|
||||
#endif
|
||||
return sizeof(*sint_ptr);
|
||||
case 2: /* ipDefaultTTL */
|
||||
*sint_ptr = IP_DEFAULT_TTL;
|
||||
return sizeof(*sint_ptr);
|
||||
case 3: /* ipInReceives */
|
||||
*uint_ptr = STATS_GET(mib2.ipinreceives);
|
||||
return sizeof(*uint_ptr);
|
||||
case 4: /* ipInHdrErrors */
|
||||
*uint_ptr = STATS_GET(mib2.ipinhdrerrors);
|
||||
return sizeof(*uint_ptr);
|
||||
case 5: /* ipInAddrErrors */
|
||||
*uint_ptr = STATS_GET(mib2.ipinaddrerrors);
|
||||
return sizeof(*uint_ptr);
|
||||
case 6: /* ipForwDatagrams */
|
||||
*uint_ptr = STATS_GET(mib2.ipforwdatagrams);
|
||||
return sizeof(*uint_ptr);
|
||||
case 7: /* ipInUnknownProtos */
|
||||
*uint_ptr = STATS_GET(mib2.ipinunknownprotos);
|
||||
return sizeof(*uint_ptr);
|
||||
case 8: /* ipInDiscards */
|
||||
*uint_ptr = STATS_GET(mib2.ipindiscards);
|
||||
return sizeof(*uint_ptr);
|
||||
case 9: /* ipInDelivers */
|
||||
*uint_ptr = STATS_GET(mib2.ipindelivers);
|
||||
return sizeof(*uint_ptr);
|
||||
case 10: /* ipOutRequests */
|
||||
*uint_ptr = STATS_GET(mib2.ipoutrequests);
|
||||
return sizeof(*uint_ptr);
|
||||
case 11: /* ipOutDiscards */
|
||||
*uint_ptr = STATS_GET(mib2.ipoutdiscards);
|
||||
return sizeof(*uint_ptr);
|
||||
case 12: /* ipOutNoRoutes */
|
||||
*uint_ptr = STATS_GET(mib2.ipoutnoroutes);
|
||||
return sizeof(*uint_ptr);
|
||||
case 13: /* ipReasmTimeout */
|
||||
#if IP_REASSEMBLY
|
||||
*sint_ptr = IP_REASS_MAXAGE;
|
||||
#else
|
||||
*sint_ptr = 0;
|
||||
#endif
|
||||
return sizeof(*sint_ptr);
|
||||
case 14: /* ipReasmReqds */
|
||||
*uint_ptr = STATS_GET(mib2.ipreasmreqds);
|
||||
return sizeof(*uint_ptr);
|
||||
case 15: /* ipReasmOKs */
|
||||
*uint_ptr = STATS_GET(mib2.ipreasmoks);
|
||||
return sizeof(*uint_ptr);
|
||||
case 16: /* ipReasmFails */
|
||||
*uint_ptr = STATS_GET(mib2.ipreasmfails);
|
||||
return sizeof(*uint_ptr);
|
||||
case 17: /* ipFragOKs */
|
||||
*uint_ptr = STATS_GET(mib2.ipfragoks);
|
||||
return sizeof(*uint_ptr);
|
||||
case 18: /* ipFragFails */
|
||||
*uint_ptr = STATS_GET(mib2.ipfragfails);
|
||||
return sizeof(*uint_ptr);
|
||||
case 19: /* ipFragCreates */
|
||||
*uint_ptr = STATS_GET(mib2.ipfragcreates);
|
||||
return sizeof(*uint_ptr);
|
||||
case 23: /* ipRoutingDiscards: not supported -> always 0 */
|
||||
*uint_ptr = 0;
|
||||
return sizeof(*uint_ptr);
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test ip object value before setting.
|
||||
*
|
||||
* @param instance node instance
|
||||
* @param len return value space (in bytes)
|
||||
* @param value points to (varbind) space to copy value from.
|
||||
*
|
||||
* @note we allow set if the value matches the hardwired value,
|
||||
* otherwise return badvalue.
|
||||
*/
|
||||
static snmp_err_t
|
||||
ip_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
|
||||
{
|
||||
snmp_err_t ret = SNMP_ERR_WRONGVALUE;
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
|
||||
LWIP_UNUSED_ARG(len);
|
||||
switch (instance->node->oid) {
|
||||
case 1: /* ipForwarding */
|
||||
#if IP_FORWARD
|
||||
/* forwarding */
|
||||
if (*sint_ptr == 1)
|
||||
#else
|
||||
/* not-forwarding */
|
||||
if (*sint_ptr == 2)
|
||||
#endif
|
||||
{
|
||||
ret = SNMP_ERR_NOERROR;
|
||||
}
|
||||
break;
|
||||
case 2: /* ipDefaultTTL */
|
||||
if (*sint_ptr == IP_DEFAULT_TTL) {
|
||||
ret = SNMP_ERR_NOERROR;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_set_test(): unknown id: %"S32_F"\n", instance->node->oid));
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
|
||||
{
|
||||
LWIP_UNUSED_ARG(instance);
|
||||
LWIP_UNUSED_ARG(len);
|
||||
LWIP_UNUSED_ARG(value);
|
||||
/* nothing to do here because in set_test we only accept values being the same as our own stored value -> no need to store anything */
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
/* --- ipAddrTable --- */
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range ip_AddrTable_oid_ranges[] = {
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff } /* IP D */
|
||||
};
|
||||
|
||||
static snmp_err_t
|
||||
ip_AddrTable_get_cell_value_core(struct netif *netif, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
switch (*column) {
|
||||
case 1: /* ipAdEntAddr */
|
||||
value->u32 = netif_ip4_addr(netif)->addr;
|
||||
break;
|
||||
case 2: /* ipAdEntIfIndex */
|
||||
value->u32 = netif_to_num(netif);
|
||||
break;
|
||||
case 3: /* ipAdEntNetMask */
|
||||
value->u32 = netif_ip4_netmask(netif)->addr;
|
||||
break;
|
||||
case 4: /* ipAdEntBcastAddr */
|
||||
/* lwIP oddity, there's no broadcast
|
||||
address in the netif we can rely on */
|
||||
value->u32 = IPADDR_BROADCAST & 1;
|
||||
break;
|
||||
case 5: /* ipAdEntReasmMaxSize */
|
||||
#if IP_REASSEMBLY
|
||||
/* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs,
|
||||
* but only if receiving one fragmented packet at a time.
|
||||
* The current solution is to calculate for 2 simultaneous packets...
|
||||
*/
|
||||
value->u32 = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) *
|
||||
(PBUF_POOL_BUFSIZE - PBUF_LINK_ENCAPSULATION_HLEN - PBUF_LINK_HLEN - IP_HLEN)));
|
||||
#else
|
||||
/** @todo returning MTU would be a bad thing and
|
||||
returning a wild guess like '576' isn't good either */
|
||||
value->u32 = 0;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_AddrTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip4_addr_t ip;
|
||||
struct netif *netif;
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, ip_AddrTable_oid_ranges, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get IP from incoming OID */
|
||||
snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
|
||||
|
||||
/* find netif with requested ip */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
if (ip4_addr_cmp(&ip, netif_ip4_addr(netif))) {
|
||||
/* fill in object properties */
|
||||
return ip_AddrTable_get_cell_value_core(netif, column, value, value_len);
|
||||
}
|
||||
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_AddrTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct netif *netif;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges)];
|
||||
snmp_ip4_to_oid(netif_ip4_addr(netif), &test_oid[0]);
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_AddrTable_oid_ranges), netif);
|
||||
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return ip_AddrTable_get_cell_value_core((struct netif*)state.reference, column, value, value_len);
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* --- ipRouteTable --- */
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range ip_RouteTable_oid_ranges[] = {
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff }, /* IP D */
|
||||
};
|
||||
|
||||
static snmp_err_t
|
||||
ip_RouteTable_get_cell_value_core(struct netif *netif, u8_t default_route, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
switch (*column) {
|
||||
case 1: /* ipRouteDest */
|
||||
if (default_route) {
|
||||
/* default rte has 0.0.0.0 dest */
|
||||
value->u32 = IP4_ADDR_ANY4->addr;
|
||||
} else {
|
||||
/* netifs have netaddress dest */
|
||||
ip4_addr_t tmp;
|
||||
ip4_addr_get_network(&tmp, netif_ip4_addr(netif), netif_ip4_netmask(netif));
|
||||
value->u32 = tmp.addr;
|
||||
}
|
||||
break;
|
||||
case 2: /* ipRouteIfIndex */
|
||||
value->u32 = netif_to_num(netif);
|
||||
break;
|
||||
case 3: /* ipRouteMetric1 */
|
||||
if (default_route) {
|
||||
value->s32 = 1; /* default */
|
||||
} else {
|
||||
value->s32 = 0; /* normal */
|
||||
}
|
||||
break;
|
||||
case 4: /* ipRouteMetric2 */
|
||||
case 5: /* ipRouteMetric3 */
|
||||
case 6: /* ipRouteMetric4 */
|
||||
value->s32 = -1; /* none */
|
||||
break;
|
||||
case 7: /* ipRouteNextHop */
|
||||
if (default_route) {
|
||||
/* default rte: gateway */
|
||||
value->u32 = netif_ip4_gw(netif)->addr;
|
||||
} else {
|
||||
/* other rtes: netif ip_addr */
|
||||
value->u32 = netif_ip4_addr(netif)->addr;
|
||||
}
|
||||
break;
|
||||
case 8: /* ipRouteType */
|
||||
if (default_route) {
|
||||
/* default rte is indirect */
|
||||
value->u32 = 4; /* indirect */
|
||||
} else {
|
||||
/* other rtes are direct */
|
||||
value->u32 = 3; /* direct */
|
||||
}
|
||||
break;
|
||||
case 9: /* ipRouteProto */
|
||||
/* locally defined routes */
|
||||
value->u32 = 2; /* local */
|
||||
break;
|
||||
case 10: /* ipRouteAge */
|
||||
/* @todo (sysuptime - timestamp last change) / 100 */
|
||||
value->u32 = 0;
|
||||
break;
|
||||
case 11: /* ipRouteMask */
|
||||
if (default_route) {
|
||||
/* default rte use 0.0.0.0 mask */
|
||||
value->u32 = IP4_ADDR_ANY4->addr;
|
||||
} else {
|
||||
/* other rtes use netmask */
|
||||
value->u32 = netif_ip4_netmask(netif)->addr;
|
||||
}
|
||||
break;
|
||||
case 12: /* ipRouteMetric5 */
|
||||
value->s32 = -1; /* none */
|
||||
break;
|
||||
case 13: /* ipRouteInfo */
|
||||
value->const_ptr = snmp_zero_dot_zero.id;
|
||||
*value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_RouteTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip4_addr_t test_ip;
|
||||
struct netif *netif;
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, ip_RouteTable_oid_ranges, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get IP and port from incoming OID */
|
||||
snmp_oid_to_ip4(&row_oid[0], &test_ip); /* we know it succeeds because of oid_in_range check above */
|
||||
|
||||
/* default route is on default netif */
|
||||
if (ip4_addr_isany_val(test_ip) && (netif_default != NULL)) {
|
||||
/* fill in object properties */
|
||||
return ip_RouteTable_get_cell_value_core(netif_default, 1, column, value, value_len);
|
||||
}
|
||||
|
||||
/* find netif with requested route */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
ip4_addr_t dst;
|
||||
ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
|
||||
|
||||
if (ip4_addr_cmp(&dst, &test_ip)) {
|
||||
/* fill in object properties */
|
||||
return ip_RouteTable_get_cell_value_core(netif, 0, column, value, value_len);
|
||||
}
|
||||
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_RouteTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct netif *netif;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges)];
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges));
|
||||
|
||||
/* check default route */
|
||||
if (netif_default != NULL) {
|
||||
snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[0]);
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif_default);
|
||||
}
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
ip4_addr_t dst;
|
||||
ip4_addr_get_network(&dst, netif_ip4_addr(netif), netif_ip4_netmask(netif));
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
if (!ip4_addr_isany_val(dst)) {
|
||||
snmp_ip4_to_oid(&dst, &test_oid[0]);
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_RouteTable_oid_ranges), netif);
|
||||
}
|
||||
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
ip4_addr_t dst;
|
||||
snmp_oid_to_ip4(&result_temp[0], &dst);
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return ip_RouteTable_get_cell_value_core((struct netif*)state.reference, ip4_addr_isany_val(dst), column, value, value_len);
|
||||
} else {
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
#if LWIP_ARP && LWIP_IPV4
|
||||
/* --- ipNetToMediaTable --- */
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range ip_NetToMediaTable_oid_ranges[] = {
|
||||
{ 1, 0xff }, /* IfIndex */
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff } /* IP D */
|
||||
};
|
||||
|
||||
static snmp_err_t
|
||||
ip_NetToMediaTable_get_cell_value_core(u8_t arp_table_index, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip4_addr_t *ip;
|
||||
struct netif *netif;
|
||||
struct eth_addr *ethaddr;
|
||||
|
||||
etharp_get_entry(arp_table_index, &ip, &netif, ðaddr);
|
||||
|
||||
/* value */
|
||||
switch (*column) {
|
||||
case 1: /* atIfIndex / ipNetToMediaIfIndex */
|
||||
value->u32 = netif_to_num(netif);
|
||||
break;
|
||||
case 2: /* atPhysAddress / ipNetToMediaPhysAddress */
|
||||
value->ptr = ethaddr;
|
||||
*value_len = sizeof(*ethaddr);
|
||||
break;
|
||||
case 3: /* atNetAddress / ipNetToMediaNetAddress */
|
||||
value->u32 = ip->addr;
|
||||
break;
|
||||
case 4: /* ipNetToMediaType */
|
||||
value->u32 = 3; /* dynamic*/
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_NetToMediaTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip4_addr_t ip_in;
|
||||
u8_t netif_index;
|
||||
u8_t i;
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, ip_NetToMediaTable_oid_ranges, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get IP from incoming OID */
|
||||
netif_index = (u8_t)row_oid[0];
|
||||
snmp_oid_to_ip4(&row_oid[1], &ip_in); /* we know it succeeds because of oid_in_range check above */
|
||||
|
||||
/* find requested entry */
|
||||
for (i=0; i<ARP_TABLE_SIZE; i++) {
|
||||
ip4_addr_t *ip;
|
||||
struct netif *netif;
|
||||
struct eth_addr *ethaddr;
|
||||
|
||||
if (etharp_get_entry(i, &ip, &netif, ðaddr)) {
|
||||
if ((netif_index == netif_to_num(netif)) && ip4_addr_cmp(&ip_in, ip)) {
|
||||
/* fill in object properties */
|
||||
return ip_NetToMediaTable_get_cell_value_core(i, column, value, value_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
ip_NetToMediaTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
u8_t i;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
for (i=0; i<ARP_TABLE_SIZE; i++) {
|
||||
ip4_addr_t *ip;
|
||||
struct netif *netif;
|
||||
struct eth_addr *ethaddr;
|
||||
|
||||
if (etharp_get_entry(i, &ip, &netif, ðaddr)) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges)];
|
||||
|
||||
test_oid[0] = netif_to_num(netif);
|
||||
snmp_ip4_to_oid(ip, &test_oid[1]);
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(ip_NetToMediaTable_oid_ranges), (void*)(size_t)i);
|
||||
}
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return ip_NetToMediaTable_get_cell_value_core((u8_t)(size_t)state.reference, column, value, value_len);
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
#endif /* LWIP_ARP && LWIP_IPV4 */
|
||||
|
||||
static const struct snmp_scalar_node ip_Forwarding = SNMP_SCALAR_CREATE_NODE(1, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
|
||||
static const struct snmp_scalar_node ip_DefaultTTL = SNMP_SCALAR_CREATE_NODE(2, SNMP_NODE_INSTANCE_READ_WRITE, SNMP_ASN1_TYPE_INTEGER, ip_get_value, ip_set_test, ip_set_value);
|
||||
static const struct snmp_scalar_node ip_InReceives = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_InHdrErrors = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_InAddrErrors = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_ForwDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_InUnknownProtos = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_InDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_InDelivers = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_OutRequests = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_OutDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_OutNoRoutes = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_ReasmTimeout = SNMP_SCALAR_CREATE_NODE_READONLY(13, SNMP_ASN1_TYPE_INTEGER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_ReasmReqds = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_ReasmOKs = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_ReasmFails = SNMP_SCALAR_CREATE_NODE_READONLY(16, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_FragOKs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_FragFails = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_FragCreates = SNMP_SCALAR_CREATE_NODE_READONLY(19, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
static const struct snmp_scalar_node ip_RoutingDiscards = SNMP_SCALAR_CREATE_NODE_READONLY(23, SNMP_ASN1_TYPE_COUNTER, ip_get_value);
|
||||
|
||||
static const struct snmp_table_simple_col_def ip_AddrTable_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntAddr */
|
||||
{ 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntIfIndex */
|
||||
{ 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntNetMask */
|
||||
{ 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipAdEntBcastAddr */
|
||||
{ 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* ipAdEntReasmMaxSize */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node ip_AddrTable = SNMP_TABLE_CREATE_SIMPLE(20, ip_AddrTable_columns, ip_AddrTable_get_cell_value, ip_AddrTable_get_next_cell_instance_and_value);
|
||||
|
||||
static const struct snmp_table_simple_col_def ip_RouteTable_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteDest */
|
||||
{ 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteIfIndex */
|
||||
{ 3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric1 */
|
||||
{ 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric2 */
|
||||
{ 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric3 */
|
||||
{ 6, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric4 */
|
||||
{ 7, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteNextHop */
|
||||
{ 8, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteType */
|
||||
{ 9, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteProto */
|
||||
{ 10, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteAge */
|
||||
{ 11, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipRouteMask */
|
||||
{ 12, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_S32 }, /* ipRouteMetric5 */
|
||||
{ 13, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_VARIANT_VALUE_TYPE_PTR } /* ipRouteInfo */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node ip_RouteTable = SNMP_TABLE_CREATE_SIMPLE(21, ip_RouteTable_columns, ip_RouteTable_get_cell_value, ip_RouteTable_get_next_cell_instance_and_value);
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
#if LWIP_ARP && LWIP_IPV4
|
||||
static const struct snmp_table_simple_col_def ip_NetToMediaTable_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaIfIndex */
|
||||
{ 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* ipNetToMediaPhysAddress */
|
||||
{ 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* ipNetToMediaNetAddress */
|
||||
{ 4, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* ipNetToMediaType */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node ip_NetToMediaTable = SNMP_TABLE_CREATE_SIMPLE(22, ip_NetToMediaTable_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
|
||||
#endif /* LWIP_ARP && LWIP_IPV4 */
|
||||
|
||||
#if LWIP_IPV4
|
||||
/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
|
||||
CREATE_LWIP_SYNC_NODE( 1, ip_Forwarding)
|
||||
CREATE_LWIP_SYNC_NODE( 2, ip_DefaultTTL)
|
||||
CREATE_LWIP_SYNC_NODE( 3, ip_InReceives)
|
||||
CREATE_LWIP_SYNC_NODE( 4, ip_InHdrErrors)
|
||||
CREATE_LWIP_SYNC_NODE( 5, ip_InAddrErrors)
|
||||
CREATE_LWIP_SYNC_NODE( 6, ip_ForwDatagrams)
|
||||
CREATE_LWIP_SYNC_NODE( 7, ip_InUnknownProtos)
|
||||
CREATE_LWIP_SYNC_NODE( 8, ip_InDiscards)
|
||||
CREATE_LWIP_SYNC_NODE( 9, ip_InDelivers)
|
||||
CREATE_LWIP_SYNC_NODE(10, ip_OutRequests)
|
||||
CREATE_LWIP_SYNC_NODE(11, ip_OutDiscards)
|
||||
CREATE_LWIP_SYNC_NODE(12, ip_OutNoRoutes)
|
||||
CREATE_LWIP_SYNC_NODE(13, ip_ReasmTimeout)
|
||||
CREATE_LWIP_SYNC_NODE(14, ip_ReasmReqds)
|
||||
CREATE_LWIP_SYNC_NODE(15, ip_ReasmOKs)
|
||||
CREATE_LWIP_SYNC_NODE(15, ip_ReasmFails)
|
||||
CREATE_LWIP_SYNC_NODE(17, ip_FragOKs)
|
||||
CREATE_LWIP_SYNC_NODE(18, ip_FragFails)
|
||||
CREATE_LWIP_SYNC_NODE(19, ip_FragCreates)
|
||||
CREATE_LWIP_SYNC_NODE(20, ip_AddrTable)
|
||||
CREATE_LWIP_SYNC_NODE(21, ip_RouteTable)
|
||||
#if LWIP_ARP
|
||||
CREATE_LWIP_SYNC_NODE(22, ip_NetToMediaTable)
|
||||
#endif /* LWIP_ARP */
|
||||
CREATE_LWIP_SYNC_NODE(23, ip_RoutingDiscards)
|
||||
|
||||
static const struct snmp_node* const ip_nodes[] = {
|
||||
&SYNC_NODE_NAME(ip_Forwarding).node.node,
|
||||
&SYNC_NODE_NAME(ip_DefaultTTL).node.node,
|
||||
&SYNC_NODE_NAME(ip_InReceives).node.node,
|
||||
&SYNC_NODE_NAME(ip_InHdrErrors).node.node,
|
||||
&SYNC_NODE_NAME(ip_InAddrErrors).node.node,
|
||||
&SYNC_NODE_NAME(ip_ForwDatagrams).node.node,
|
||||
&SYNC_NODE_NAME(ip_InUnknownProtos).node.node,
|
||||
&SYNC_NODE_NAME(ip_InDiscards).node.node,
|
||||
&SYNC_NODE_NAME(ip_InDelivers).node.node,
|
||||
&SYNC_NODE_NAME(ip_OutRequests).node.node,
|
||||
&SYNC_NODE_NAME(ip_OutDiscards).node.node,
|
||||
&SYNC_NODE_NAME(ip_OutNoRoutes).node.node,
|
||||
&SYNC_NODE_NAME(ip_ReasmTimeout).node.node,
|
||||
&SYNC_NODE_NAME(ip_ReasmReqds).node.node,
|
||||
&SYNC_NODE_NAME(ip_ReasmOKs).node.node,
|
||||
&SYNC_NODE_NAME(ip_ReasmFails).node.node,
|
||||
&SYNC_NODE_NAME(ip_FragOKs).node.node,
|
||||
&SYNC_NODE_NAME(ip_FragFails).node.node,
|
||||
&SYNC_NODE_NAME(ip_FragCreates).node.node,
|
||||
&SYNC_NODE_NAME(ip_AddrTable).node.node,
|
||||
&SYNC_NODE_NAME(ip_RouteTable).node.node,
|
||||
#if LWIP_ARP
|
||||
&SYNC_NODE_NAME(ip_NetToMediaTable).node.node,
|
||||
#endif /* LWIP_ARP */
|
||||
&SYNC_NODE_NAME(ip_RoutingDiscards).node.node
|
||||
};
|
||||
|
||||
const struct snmp_tree_node snmp_mib2_ip_root = SNMP_CREATE_TREE_NODE(4, ip_nodes);
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
/* --- at .1.3.6.1.2.1.3 ----------------------------------------------------- */
|
||||
|
||||
#if LWIP_ARP && LWIP_IPV4
|
||||
/* at node table is a subset of ip_nettomedia table (same rows but less columns) */
|
||||
static const struct snmp_table_simple_col_def at_Table_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* atIfIndex */
|
||||
{ 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_VARIANT_VALUE_TYPE_PTR }, /* atPhysAddress */
|
||||
{ 3, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 } /* atNetAddress */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node at_Table = SNMP_TABLE_CREATE_SIMPLE(1, at_Table_columns, ip_NetToMediaTable_get_cell_value, ip_NetToMediaTable_get_next_cell_instance_and_value);
|
||||
|
||||
/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
|
||||
CREATE_LWIP_SYNC_NODE(1, at_Table)
|
||||
|
||||
static const struct snmp_node* const at_nodes[] = {
|
||||
&SYNC_NODE_NAME(at_Table).node.node
|
||||
};
|
||||
|
||||
const struct snmp_tree_node snmp_mib2_at_root = SNMP_CREATE_TREE_NODE(3, at_nodes);
|
||||
#endif /* LWIP_ARP && LWIP_IPV4 */
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
|
||||
227
src/apps/snmp/snmp_mib2_snmp.c
Normal file
227
src/apps/snmp/snmp_mib2_snmp.c
Normal file
@@ -0,0 +1,227 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) SNMP objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2
|
||||
|
||||
#define MIB2_AUTH_TRAPS_ENABLED 1
|
||||
#define MIB2_AUTH_TRAPS_DISABLED 2
|
||||
|
||||
/* --- snmp .1.3.6.1.2.1.11 ----------------------------------------------------- */
|
||||
static s16_t
|
||||
snmp_get_value(const struct snmp_scalar_array_node_def *node, void *value)
|
||||
{
|
||||
u32_t *uint_ptr = (u32_t*)value;
|
||||
switch (node->oid) {
|
||||
case 1: /* snmpInPkts */
|
||||
*uint_ptr = snmp_stats.inpkts;
|
||||
break;
|
||||
case 2: /* snmpOutPkts */
|
||||
*uint_ptr = snmp_stats.outpkts;
|
||||
break;
|
||||
case 3: /* snmpInBadVersions */
|
||||
*uint_ptr = snmp_stats.inbadversions;
|
||||
break;
|
||||
case 4: /* snmpInBadCommunityNames */
|
||||
*uint_ptr = snmp_stats.inbadcommunitynames;
|
||||
break;
|
||||
case 5: /* snmpInBadCommunityUses */
|
||||
*uint_ptr = snmp_stats.inbadcommunityuses;
|
||||
break;
|
||||
case 6: /* snmpInASNParseErrs */
|
||||
*uint_ptr = snmp_stats.inasnparseerrs;
|
||||
break;
|
||||
case 8: /* snmpInTooBigs */
|
||||
*uint_ptr = snmp_stats.intoobigs;
|
||||
break;
|
||||
case 9: /* snmpInNoSuchNames */
|
||||
*uint_ptr = snmp_stats.innosuchnames;
|
||||
break;
|
||||
case 10: /* snmpInBadValues */
|
||||
*uint_ptr = snmp_stats.inbadvalues;
|
||||
break;
|
||||
case 11: /* snmpInReadOnlys */
|
||||
*uint_ptr = snmp_stats.inreadonlys;
|
||||
break;
|
||||
case 12: /* snmpInGenErrs */
|
||||
*uint_ptr = snmp_stats.ingenerrs;
|
||||
break;
|
||||
case 13: /* snmpInTotalReqVars */
|
||||
*uint_ptr = snmp_stats.intotalreqvars;
|
||||
break;
|
||||
case 14: /* snmpInTotalSetVars */
|
||||
*uint_ptr = snmp_stats.intotalsetvars;
|
||||
break;
|
||||
case 15: /* snmpInGetRequests */
|
||||
*uint_ptr = snmp_stats.ingetrequests;
|
||||
break;
|
||||
case 16: /* snmpInGetNexts */
|
||||
*uint_ptr = snmp_stats.ingetnexts;
|
||||
break;
|
||||
case 17: /* snmpInSetRequests */
|
||||
*uint_ptr = snmp_stats.insetrequests;
|
||||
break;
|
||||
case 18: /* snmpInGetResponses */
|
||||
*uint_ptr = snmp_stats.ingetresponses;
|
||||
break;
|
||||
case 19: /* snmpInTraps */
|
||||
*uint_ptr = snmp_stats.intraps;
|
||||
break;
|
||||
case 20: /* snmpOutTooBigs */
|
||||
*uint_ptr = snmp_stats.outtoobigs;
|
||||
break;
|
||||
case 21: /* snmpOutNoSuchNames */
|
||||
*uint_ptr = snmp_stats.outnosuchnames;
|
||||
break;
|
||||
case 22: /* snmpOutBadValues */
|
||||
*uint_ptr = snmp_stats.outbadvalues;
|
||||
break;
|
||||
case 24: /* snmpOutGenErrs */
|
||||
*uint_ptr = snmp_stats.outgenerrs;
|
||||
break;
|
||||
case 25: /* snmpOutGetRequests */
|
||||
*uint_ptr = snmp_stats.outgetrequests;
|
||||
break;
|
||||
case 26: /* snmpOutGetNexts */
|
||||
*uint_ptr = snmp_stats.outgetnexts;
|
||||
break;
|
||||
case 27: /* snmpOutSetRequests */
|
||||
*uint_ptr = snmp_stats.outsetrequests;
|
||||
break;
|
||||
case 28: /* snmpOutGetResponses */
|
||||
*uint_ptr = snmp_stats.outgetresponses;
|
||||
break;
|
||||
case 29: /* snmpOutTraps */
|
||||
*uint_ptr = snmp_stats.outtraps;
|
||||
break;
|
||||
case 30: /* snmpEnableAuthenTraps */
|
||||
if (snmp_get_auth_traps_enabled() == SNMP_AUTH_TRAPS_DISABLED) {
|
||||
*uint_ptr = MIB2_AUTH_TRAPS_DISABLED;
|
||||
} else {
|
||||
*uint_ptr = MIB2_AUTH_TRAPS_ENABLED;
|
||||
}
|
||||
break;
|
||||
case 31: /* snmpSilentDrops */
|
||||
*uint_ptr = 0; /* not supported */
|
||||
break;
|
||||
case 32: /* snmpProxyDrops */
|
||||
*uint_ptr = 0; /* not supported */
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_value(): unknown id: %"S32_F"\n", node->oid));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sizeof(*uint_ptr);
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
snmp_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
|
||||
{
|
||||
snmp_err_t ret = SNMP_ERR_WRONGVALUE;
|
||||
LWIP_UNUSED_ARG(len);
|
||||
|
||||
if (node->oid == 30) {
|
||||
/* snmpEnableAuthenTraps */
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
|
||||
/* we should have writable non-volatile mem here */
|
||||
if ((*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) || (*sint_ptr == MIB2_AUTH_TRAPS_ENABLED)) {
|
||||
ret = SNMP_ERR_NOERROR;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
snmp_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
|
||||
{
|
||||
LWIP_UNUSED_ARG(len);
|
||||
|
||||
if (node->oid == 30) {
|
||||
/* snmpEnableAuthenTraps */
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
if (*sint_ptr == MIB2_AUTH_TRAPS_DISABLED) {
|
||||
snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_DISABLED);
|
||||
} else {
|
||||
snmp_set_auth_traps_enabled(SNMP_AUTH_TRAPS_ENABLED);
|
||||
}
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
/* the following nodes access variables in SNMP stack (snmp_stats) from SNMP worker thread -> OK, no sync needed */
|
||||
static const struct snmp_scalar_array_node_def snmp_nodes[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInPkts */
|
||||
{ 2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutPkts */
|
||||
{ 3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadVersions */
|
||||
{ 4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityNames */
|
||||
{ 5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadCommunityUses */
|
||||
{ 6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInASNParseErrs */
|
||||
{ 8, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTooBigs */
|
||||
{ 9, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInNoSuchNames */
|
||||
{10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInBadValues */
|
||||
{11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInReadOnlys */
|
||||
{12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGenErrs */
|
||||
{13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalReqVars */
|
||||
{14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTotalSetVars */
|
||||
{15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetRequests */
|
||||
{16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetNexts */
|
||||
{17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInSetRequests */
|
||||
{18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInGetResponses */
|
||||
{19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpInTraps */
|
||||
{20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTooBigs */
|
||||
{21, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutNoSuchNames */
|
||||
{22, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutBadValues */
|
||||
{24, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGenErrs */
|
||||
{25, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetRequests */
|
||||
{26, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetNexts */
|
||||
{27, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutSetRequests */
|
||||
{28, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutGetResponses */
|
||||
{29, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpOutTraps */
|
||||
{30, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE}, /* snmpEnableAuthenTraps */
|
||||
{31, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpSilentDrops */
|
||||
{32, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY} /* snmpProxyDrops */
|
||||
};
|
||||
|
||||
const struct snmp_scalar_array_node snmp_mib2_snmp_root = SNMP_SCALAR_CREATE_ARRAY_NODE(11, snmp_nodes, snmp_get_value, snmp_set_test, snmp_set_value);
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
|
||||
377
src/apps/snmp/snmp_mib2_system.c
Normal file
377
src/apps/snmp/snmp_mib2_system.c
Normal file
@@ -0,0 +1,377 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) SYSTEM objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#define SYNC_NODE_NAME(node_name) node_name ## _synced
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
|
||||
static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
|
||||
#else
|
||||
#define SYNC_NODE_NAME(node_name) node_name
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name)
|
||||
#endif
|
||||
|
||||
/* --- system .1.3.6.1.2.1.1 ----------------------------------------------------- */
|
||||
|
||||
/** mib-2.system.sysDescr */
|
||||
static const u8_t sysdescr_default[] = SNMP_LWIP_MIB2_SYSDESC;
|
||||
static const u8_t* sysdescr = sysdescr_default;
|
||||
static const u16_t* sysdescr_len = NULL; /* use strlen for determining len */
|
||||
|
||||
/** mib-2.system.sysContact */
|
||||
static const u8_t syscontact_default[] = SNMP_LWIP_MIB2_SYSCONTACT;
|
||||
static const u8_t* syscontact = syscontact_default;
|
||||
static const u16_t* syscontact_len = NULL; /* use strlen for determining len */
|
||||
static u8_t* syscontact_wr = NULL; /* if writable, points to the same buffer as syscontact (required for correct constness) */
|
||||
static u16_t* syscontact_wr_len = NULL; /* if writable, points to the same buffer as syscontact_len (required for correct constness) */
|
||||
static u16_t syscontact_bufsize = 0; /* 0=not writable */
|
||||
|
||||
/** mib-2.system.sysName */
|
||||
static const u8_t sysname_default[] = SNMP_LWIP_MIB2_SYSNAME;
|
||||
static const u8_t* sysname = sysname_default;
|
||||
static const u16_t* sysname_len = NULL; /* use strlen for determining len */
|
||||
static u8_t* sysname_wr = NULL; /* if writable, points to the same buffer as sysname (required for correct constness) */
|
||||
static u16_t* sysname_wr_len = NULL; /* if writable, points to the same buffer as sysname_len (required for correct constness) */
|
||||
static u16_t sysname_bufsize = 0; /* 0=not writable */
|
||||
|
||||
/** mib-2.system.sysLocation */
|
||||
static const u8_t syslocation_default[] = SNMP_LWIP_MIB2_SYSLOCATION;
|
||||
static const u8_t* syslocation = syslocation_default;
|
||||
static const u16_t* syslocation_len = NULL; /* use strlen for determining len */
|
||||
static u8_t* syslocation_wr = NULL; /* if writable, points to the same buffer as syslocation (required for correct constness) */
|
||||
static u16_t* syslocation_wr_len = NULL; /* if writable, points to the same buffer as syslocation_len (required for correct constness) */
|
||||
static u16_t syslocation_bufsize = 0; /* 0=not writable */
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* Initializes sysDescr pointers.
|
||||
*
|
||||
* @param str if non-NULL then copy str pointer
|
||||
* @param len points to string length, excluding zero terminator
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_sysdescr(const u8_t *str, const u16_t *len)
|
||||
{
|
||||
if (str != NULL) {
|
||||
sysdescr = str;
|
||||
sysdescr_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* Initializes sysContact pointers
|
||||
*
|
||||
* @param ocstr if non-NULL then copy str pointer
|
||||
* @param ocstrlen points to string length, excluding zero terminator.
|
||||
* if set to NULL it is assumed that ocstr is NULL-terminated.
|
||||
* @param bufsize size of the buffer in bytes.
|
||||
* (this is required because the buffer can be overwritten by snmp-set)
|
||||
* if ocstrlen is NULL buffer needs space for terminating 0 byte.
|
||||
* otherwise complete buffer is used for string.
|
||||
* if bufsize is set to 0, the value is regarded as read-only.
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_syscontact(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
syscontact = ocstr;
|
||||
syscontact_wr = ocstr;
|
||||
syscontact_len = ocstrlen;
|
||||
syscontact_wr_len = ocstrlen;
|
||||
syscontact_bufsize = bufsize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* see \ref snmp_mib2_set_syscontact but set pointer to readonly memory
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_syscontact_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
syscontact = ocstr;
|
||||
syscontact_len = ocstrlen;
|
||||
syscontact_wr = NULL;
|
||||
syscontact_wr_len = NULL;
|
||||
syscontact_bufsize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* Initializes sysName pointers
|
||||
*
|
||||
* @param ocstr if non-NULL then copy str pointer
|
||||
* @param ocstrlen points to string length, excluding zero terminator.
|
||||
* if set to NULL it is assumed that ocstr is NULL-terminated.
|
||||
* @param bufsize size of the buffer in bytes.
|
||||
* (this is required because the buffer can be overwritten by snmp-set)
|
||||
* if ocstrlen is NULL buffer needs space for terminating 0 byte.
|
||||
* otherwise complete buffer is used for string.
|
||||
* if bufsize is set to 0, the value is regarded as read-only.
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_sysname(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
sysname = ocstr;
|
||||
sysname_wr = ocstr;
|
||||
sysname_len = ocstrlen;
|
||||
sysname_wr_len = ocstrlen;
|
||||
sysname_bufsize = bufsize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* see \ref snmp_mib2_set_sysname but set pointer to readonly memory
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_sysname_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
sysname = ocstr;
|
||||
sysname_len = ocstrlen;
|
||||
sysname_wr = NULL;
|
||||
sysname_wr_len = NULL;
|
||||
sysname_bufsize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* Initializes sysLocation pointers
|
||||
*
|
||||
* @param ocstr if non-NULL then copy str pointer
|
||||
* @param ocstrlen points to string length, excluding zero terminator.
|
||||
* if set to NULL it is assumed that ocstr is NULL-terminated.
|
||||
* @param bufsize size of the buffer in bytes.
|
||||
* (this is required because the buffer can be overwritten by snmp-set)
|
||||
* if ocstrlen is NULL buffer needs space for terminating 0 byte.
|
||||
* otherwise complete buffer is used for string.
|
||||
* if bufsize is set to 0, the value is regarded as read-only.
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_syslocation(u8_t *ocstr, u16_t *ocstrlen, u16_t bufsize)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
syslocation = ocstr;
|
||||
syslocation_wr = ocstr;
|
||||
syslocation_len = ocstrlen;
|
||||
syslocation_wr_len = ocstrlen;
|
||||
syslocation_bufsize = bufsize;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_mib2
|
||||
* see \ref snmp_mib2_set_syslocation but set pointer to readonly memory
|
||||
*/
|
||||
void
|
||||
snmp_mib2_set_syslocation_readonly(const u8_t *ocstr, const u16_t *ocstrlen)
|
||||
{
|
||||
if (ocstr != NULL) {
|
||||
syslocation = ocstr;
|
||||
syslocation_len = ocstrlen;
|
||||
syslocation_wr = NULL;
|
||||
syslocation_wr_len = NULL;
|
||||
syslocation_bufsize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static s16_t
|
||||
system_get_value(const struct snmp_scalar_array_node_def *node, void *value)
|
||||
{
|
||||
const u8_t* var = NULL;
|
||||
const s16_t* var_len;
|
||||
u16_t result;
|
||||
|
||||
switch (node->oid) {
|
||||
case 1: /* sysDescr */
|
||||
var = sysdescr;
|
||||
var_len = (const s16_t*)sysdescr_len;
|
||||
break;
|
||||
case 2: /* sysObjectID */
|
||||
{
|
||||
const struct snmp_obj_id* dev_enterprise_oid = snmp_get_device_enterprise_oid();
|
||||
MEMCPY(value, dev_enterprise_oid->id, dev_enterprise_oid->len * sizeof(u32_t));
|
||||
return dev_enterprise_oid->len * sizeof(u32_t);
|
||||
}
|
||||
case 3: /* sysUpTime */
|
||||
MIB2_COPY_SYSUPTIME_TO((u32_t*)value);
|
||||
return sizeof(u32_t);
|
||||
case 4: /* sysContact */
|
||||
var = syscontact;
|
||||
var_len = (const s16_t*)syscontact_len;
|
||||
break;
|
||||
case 5: /* sysName */
|
||||
var = sysname;
|
||||
var_len = (const s16_t*)sysname_len;
|
||||
break;
|
||||
case 6: /* sysLocation */
|
||||
var = syslocation;
|
||||
var_len = (const s16_t*)syslocation_len;
|
||||
break;
|
||||
case 7: /* sysServices */
|
||||
*(s32_t*)value = SNMP_SYSSERVICES;
|
||||
return sizeof(s32_t);
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_value(): unknown id: %"S32_F"\n", node->oid));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handle string values (OID 1,4,5 and 6) */
|
||||
LWIP_ASSERT("", (value != NULL));
|
||||
if (var_len == NULL) {
|
||||
result = (s16_t)strlen((const char*)var);
|
||||
} else {
|
||||
result = *var_len;
|
||||
}
|
||||
MEMCPY(value, var, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
system_set_test(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
|
||||
{
|
||||
snmp_err_t ret = SNMP_ERR_WRONGVALUE;
|
||||
const u16_t* var_bufsize = NULL;
|
||||
const u16_t* var_wr_len;
|
||||
|
||||
LWIP_UNUSED_ARG(value);
|
||||
|
||||
switch (node->oid) {
|
||||
case 4: /* sysContact */
|
||||
var_bufsize = &syscontact_bufsize;
|
||||
var_wr_len = syscontact_wr_len;
|
||||
break;
|
||||
case 5: /* sysName */
|
||||
var_bufsize = &sysname_bufsize;
|
||||
var_wr_len = sysname_wr_len;
|
||||
break;
|
||||
case 6: /* sysLocation */
|
||||
var_bufsize = &syslocation_bufsize;
|
||||
var_wr_len = syslocation_wr_len;
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_test(): unknown id: %"S32_F"\n", node->oid));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* check if value is writable at all */
|
||||
if (*var_bufsize > 0) {
|
||||
if (var_wr_len == NULL) {
|
||||
/* we have to take the terminating 0 into account */
|
||||
if (len < *var_bufsize) {
|
||||
ret = SNMP_ERR_NOERROR;
|
||||
}
|
||||
} else {
|
||||
if (len <= *var_bufsize) {
|
||||
ret = SNMP_ERR_NOERROR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = SNMP_ERR_NOTWRITABLE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
system_set_value(const struct snmp_scalar_array_node_def *node, u16_t len, void *value)
|
||||
{
|
||||
u8_t* var_wr = NULL;
|
||||
u16_t* var_wr_len;
|
||||
|
||||
switch (node->oid) {
|
||||
case 4: /* sysContact */
|
||||
var_wr = syscontact_wr;
|
||||
var_wr_len = syscontact_wr_len;
|
||||
break;
|
||||
case 5: /* sysName */
|
||||
var_wr = sysname_wr;
|
||||
var_wr_len = sysname_wr_len;
|
||||
break;
|
||||
case 6: /* sysLocation */
|
||||
var_wr = syslocation_wr;
|
||||
var_wr_len = syslocation_wr_len;
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_set_value(): unknown id: %"S32_F"\n", node->oid));
|
||||
return SNMP_ERR_GENERROR;
|
||||
}
|
||||
|
||||
/* no need to check size of target buffer, this was already done in set_test method */
|
||||
LWIP_ASSERT("", var_wr != NULL);
|
||||
MEMCPY(var_wr, value, len);
|
||||
|
||||
if (var_wr_len == NULL) {
|
||||
/* add terminating 0 */
|
||||
var_wr[len] = 0;
|
||||
} else {
|
||||
*var_wr_len = len;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static const struct snmp_scalar_array_node_def system_nodes[] = {
|
||||
{1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysDescr */
|
||||
{2, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysObjectID */
|
||||
{3, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY}, /* sysUpTime */
|
||||
{4, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysContact */
|
||||
{5, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysName */
|
||||
{6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_WRITE}, /* sysLocation */
|
||||
{7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY} /* sysServices */
|
||||
};
|
||||
|
||||
const struct snmp_scalar_array_node snmp_mib2_system_node = SNMP_SCALAR_CREATE_ARRAY_NODE(1, system_nodes, system_get_value, system_set_test, system_set_value);
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
|
||||
594
src/apps/snmp/snmp_mib2_tcp.c
Normal file
594
src/apps/snmp/snmp_mib2_tcp.c
Normal file
@@ -0,0 +1,594 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) TCP objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#define SYNC_NODE_NAME(node_name) node_name ## _synced
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
|
||||
static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
|
||||
#else
|
||||
#define SYNC_NODE_NAME(node_name) node_name
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name)
|
||||
#endif
|
||||
|
||||
/* --- tcp .1.3.6.1.2.1.6 ----------------------------------------------------- */
|
||||
|
||||
static s16_t
|
||||
tcp_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
u32_t *uint_ptr = (u32_t*)value;
|
||||
s32_t *sint_ptr = (s32_t*)value;
|
||||
|
||||
switch (instance->node->oid) {
|
||||
case 1: /* tcpRtoAlgorithm, vanj(4) */
|
||||
*sint_ptr = 4;
|
||||
return sizeof(*sint_ptr);
|
||||
case 2: /* tcpRtoMin */
|
||||
/* @todo not the actual value, a guess,
|
||||
needs to be calculated */
|
||||
*sint_ptr = 1000;
|
||||
return sizeof(*sint_ptr);
|
||||
case 3: /* tcpRtoMax */
|
||||
/* @todo not the actual value, a guess,
|
||||
needs to be calculated */
|
||||
*sint_ptr = 60000;
|
||||
return sizeof(*sint_ptr);
|
||||
case 4: /* tcpMaxConn */
|
||||
*sint_ptr = MEMP_NUM_TCP_PCB;
|
||||
return sizeof(*sint_ptr);
|
||||
case 5: /* tcpActiveOpens */
|
||||
*uint_ptr = STATS_GET(mib2.tcpactiveopens);
|
||||
return sizeof(*uint_ptr);
|
||||
case 6: /* tcpPassiveOpens */
|
||||
*uint_ptr = STATS_GET(mib2.tcppassiveopens);
|
||||
return sizeof(*uint_ptr);
|
||||
case 7: /* tcpAttemptFails */
|
||||
*uint_ptr = STATS_GET(mib2.tcpattemptfails);
|
||||
return sizeof(*uint_ptr);
|
||||
case 8: /* tcpEstabResets */
|
||||
*uint_ptr = STATS_GET(mib2.tcpestabresets);
|
||||
return sizeof(*uint_ptr);
|
||||
case 9: /* tcpCurrEstab */
|
||||
{
|
||||
u16_t tcpcurrestab = 0;
|
||||
struct tcp_pcb *pcb = tcp_active_pcbs;
|
||||
while (pcb != NULL) {
|
||||
if ((pcb->state == ESTABLISHED) ||
|
||||
(pcb->state == CLOSE_WAIT)) {
|
||||
tcpcurrestab++;
|
||||
}
|
||||
pcb = pcb->next;
|
||||
}
|
||||
*uint_ptr = tcpcurrestab;
|
||||
}
|
||||
return sizeof(*uint_ptr);
|
||||
case 10: /* tcpInSegs */
|
||||
*uint_ptr = STATS_GET(mib2.tcpinsegs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 11: /* tcpOutSegs */
|
||||
*uint_ptr = STATS_GET(mib2.tcpoutsegs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 12: /* tcpRetransSegs */
|
||||
*uint_ptr = STATS_GET(mib2.tcpretranssegs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 14: /* tcpInErrs */
|
||||
*uint_ptr = STATS_GET(mib2.tcpinerrs);
|
||||
return sizeof(*uint_ptr);
|
||||
case 15: /* tcpOutRsts */
|
||||
*uint_ptr = STATS_GET(mib2.tcpoutrsts);
|
||||
return sizeof(*uint_ptr);
|
||||
case 17: /* tcpHCInSegs */
|
||||
memset(value, 0, 2*sizeof(u32_t)); /* not supported */
|
||||
return 2*sizeof(u32_t);
|
||||
case 18: /* tcpHCOutSegs */
|
||||
memset(value, 0, 2*sizeof(u32_t)); /* not supported */
|
||||
return 2*sizeof(u32_t);
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --- tcpConnTable --- */
|
||||
|
||||
#if LWIP_IPV4
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range tcp_ConnTable_oid_ranges[] = {
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff }, /* IP D */
|
||||
{ 0, 0xffff }, /* Port */
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff }, /* IP D */
|
||||
{ 0, 0xffff } /* Port */
|
||||
};
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnTable_get_cell_value_core(struct tcp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* value */
|
||||
switch (*column) {
|
||||
case 1: /* tcpConnState */
|
||||
value->u32 = pcb->state + 1;
|
||||
break;
|
||||
case 2: /* tcpConnLocalAddress */
|
||||
value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
|
||||
break;
|
||||
case 3: /* tcpConnLocalPort */
|
||||
value->u32 = pcb->local_port;
|
||||
break;
|
||||
case 4: /* tcpConnRemAddress */
|
||||
if (pcb->state == LISTEN) {
|
||||
value->u32 = IP4_ADDR_ANY4->addr;
|
||||
} else {
|
||||
value->u32 = ip_2_ip4(&pcb->remote_ip)->addr;
|
||||
}
|
||||
break;
|
||||
case 5: /* tcpConnRemPort */
|
||||
if (pcb->state == LISTEN) {
|
||||
value->u32 = 0;
|
||||
} else {
|
||||
value->u32 = pcb->remote_port;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LWIP_ASSERT("invalid id", 0);
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
u8_t i;
|
||||
ip4_addr_t local_ip;
|
||||
ip4_addr_t remote_ip;
|
||||
u16_t local_port;
|
||||
u16_t remote_port;
|
||||
struct tcp_pcb *pcb;
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, tcp_ConnTable_oid_ranges, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get IPs and ports from incoming OID */
|
||||
snmp_oid_to_ip4(&row_oid[0], &local_ip); /* we know it succeeds because of oid_in_range check above */
|
||||
local_port = (u16_t)row_oid[4];
|
||||
snmp_oid_to_ip4(&row_oid[5], &remote_ip); /* we know it succeeds because of oid_in_range check above */
|
||||
remote_port = (u16_t)row_oid[9];
|
||||
|
||||
/* find tcp_pcb with requested ips and ports */
|
||||
for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
|
||||
pcb = *tcp_pcb_lists[i];
|
||||
|
||||
while (pcb != NULL) {
|
||||
/* do local IP and local port match? */
|
||||
if (IP_IS_V4_VAL(pcb->local_ip) &&
|
||||
ip4_addr_cmp(&local_ip, ip_2_ip4(&pcb->local_ip)) && (local_port == pcb->local_port)) {
|
||||
|
||||
/* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
|
||||
if (pcb->state == LISTEN) {
|
||||
if (ip4_addr_cmp(&remote_ip, IP4_ADDR_ANY4) && (remote_port == 0)) {
|
||||
/* fill in object properties */
|
||||
return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
|
||||
}
|
||||
} else {
|
||||
if (IP_IS_V4_VAL(pcb->remote_ip) &&
|
||||
ip4_addr_cmp(&remote_ip, ip_2_ip4(&pcb->remote_ip)) && (remote_port == pcb->remote_port)) {
|
||||
/* fill in object properties */
|
||||
return tcp_ConnTable_get_cell_value_core(pcb, column, value, value_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
u8_t i;
|
||||
struct tcp_pcb *pcb;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_lists); i++) {
|
||||
pcb = *tcp_pcb_lists[i];
|
||||
while (pcb != NULL) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges)];
|
||||
|
||||
if (IP_IS_V4_VAL(pcb->local_ip)) {
|
||||
snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
|
||||
test_oid[4] = pcb->local_port;
|
||||
|
||||
/* PCBs in state LISTEN are not connected and have no remote_ip or remote_port */
|
||||
if (pcb->state == LISTEN) {
|
||||
snmp_ip4_to_oid(IP4_ADDR_ANY4, &test_oid[5]);
|
||||
test_oid[9] = 0;
|
||||
} else {
|
||||
if (IP_IS_V6_VAL(pcb->remote_ip)) { /* should never happen */
|
||||
continue;
|
||||
}
|
||||
snmp_ip4_to_oid(ip_2_ip4(&pcb->remote_ip), &test_oid[5]);
|
||||
test_oid[9] = pcb->remote_port;
|
||||
}
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(tcp_ConnTable_oid_ranges), pcb);
|
||||
}
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return tcp_ConnTable_get_cell_value_core((struct tcp_pcb*)state.reference, column, value, value_len);
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
/* --- tcpConnectionTable --- */
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnectionTable_get_cell_value_core(const u32_t* column, struct tcp_pcb *pcb, union snmp_variant_value* value)
|
||||
{
|
||||
/* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
|
||||
switch (*column) {
|
||||
case 7: /* tcpConnectionState */
|
||||
value->u32 = pcb->state + 1;
|
||||
break;
|
||||
case 8: /* tcpConnectionProcess */
|
||||
value->u32 = 0; /* not supported */
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnectionTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip_addr_t local_ip, remote_ip;
|
||||
u16_t local_port, remote_port;
|
||||
struct tcp_pcb *pcb;
|
||||
u8_t idx = 0;
|
||||
u8_t i;
|
||||
struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
|
||||
idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
|
||||
if (idx == 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
|
||||
idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port);
|
||||
if (idx == 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* find tcp_pcb with requested ip and port*/
|
||||
for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
|
||||
pcb = *tcp_pcb_nonlisten_lists[i];
|
||||
|
||||
while (pcb != NULL) {
|
||||
if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
|
||||
(local_port == pcb->local_port) &&
|
||||
ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
|
||||
(remote_port == pcb->remote_port)) {
|
||||
/* fill in object properties */
|
||||
return tcp_ConnectionTable_get_cell_value_core(column, pcb, value);
|
||||
}
|
||||
pcb = pcb->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ConnectionTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct tcp_pcb *pcb;
|
||||
struct snmp_next_oid_state state;
|
||||
/* 1x tcpConnectionLocalAddressType + 1x OID len + 16x tcpConnectionLocalAddress + 1x tcpConnectionLocalPort
|
||||
* 1x tcpConnectionRemAddressType + 1x OID len + 16x tcpConnectionRemAddress + 1x tcpConnectionRemPort */
|
||||
u32_t result_temp[38];
|
||||
u8_t i;
|
||||
struct tcp_pcb ** const tcp_pcb_nonlisten_lists[] = {&tcp_bound_pcbs, &tcp_active_pcbs, &tcp_tw_pcbs};
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
for (i = 0; i < LWIP_ARRAYSIZE(tcp_pcb_nonlisten_lists); i++) {
|
||||
pcb = *tcp_pcb_nonlisten_lists[i];
|
||||
|
||||
while (pcb != NULL) {
|
||||
u8_t idx = 0;
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
|
||||
|
||||
/* tcpConnectionLocalAddressType + tcpConnectionLocalAddress + tcpConnectionLocalPort */
|
||||
idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
|
||||
|
||||
/* tcpConnectionRemAddressType + tcpConnectionRemAddress + tcpConnectionRemPort */
|
||||
idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, idx, pcb);
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return tcp_ConnectionTable_get_cell_value_core(column, (struct tcp_pcb*)state.reference, value);
|
||||
} else {
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- tcpListenerTable --- */
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ListenerTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value)
|
||||
{
|
||||
/* all items except tcpListenerProcess are declared as not-accessible */
|
||||
switch (*column) {
|
||||
case 4: /* tcpListenerProcess */
|
||||
value->u32 = 0; /* not supported */
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ListenerTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip_addr_t local_ip;
|
||||
u16_t local_port;
|
||||
struct tcp_pcb_listen *pcb;
|
||||
u8_t idx = 0;
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
|
||||
idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
|
||||
if (idx == 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* find tcp_pcb with requested ip and port*/
|
||||
pcb = tcp_listen_pcbs.listen_pcbs;
|
||||
while (pcb != NULL) {
|
||||
if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
|
||||
(local_port == pcb->local_port)) {
|
||||
/* fill in object properties */
|
||||
return tcp_ListenerTable_get_cell_value_core(column, value);
|
||||
}
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
tcp_ListenerTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct tcp_pcb_listen *pcb;
|
||||
struct snmp_next_oid_state state;
|
||||
/* 1x tcpListenerLocalAddressType + 1x OID len + 16x tcpListenerLocalAddress + 1x tcpListenerLocalPort */
|
||||
u32_t result_temp[19];
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
pcb = tcp_listen_pcbs.listen_pcbs;
|
||||
while (pcb != NULL) {
|
||||
u8_t idx = 0;
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
|
||||
|
||||
/* tcpListenerLocalAddressType + tcpListenerLocalAddress + tcpListenerLocalPort */
|
||||
idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, idx, NULL);
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return tcp_ListenerTable_get_cell_value_core(column, value);
|
||||
} else {
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct snmp_scalar_node tcp_RtoAlgorithm = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_RtoMin = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_RtoMax = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_MaxConn = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_INTEGER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_ActiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(5, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_PassiveOpens = SNMP_SCALAR_CREATE_NODE_READONLY(6, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_AttemptFails = SNMP_SCALAR_CREATE_NODE_READONLY(7, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_EstabResets = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_CurrEstab = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_GAUGE, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_InSegs = SNMP_SCALAR_CREATE_NODE_READONLY(10, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_OutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(11, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_RetransSegs = SNMP_SCALAR_CREATE_NODE_READONLY(12, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_InErrs = SNMP_SCALAR_CREATE_NODE_READONLY(14, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_OutRsts = SNMP_SCALAR_CREATE_NODE_READONLY(15, SNMP_ASN1_TYPE_COUNTER, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_HCInSegs = SNMP_SCALAR_CREATE_NODE_READONLY(17, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
|
||||
static const struct snmp_scalar_node tcp_HCOutSegs = SNMP_SCALAR_CREATE_NODE_READONLY(18, SNMP_ASN1_TYPE_COUNTER64, tcp_get_value);
|
||||
|
||||
#if LWIP_IPV4
|
||||
static const struct snmp_table_simple_col_def tcp_ConnTable_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnState */
|
||||
{ 2, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalAddress */
|
||||
{ 3, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnLocalPort */
|
||||
{ 4, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnRemAddress */
|
||||
{ 5, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnRemPort */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node tcp_ConnTable = SNMP_TABLE_CREATE_SIMPLE(13, tcp_ConnTable_columns, tcp_ConnTable_get_cell_value, tcp_ConnTable_get_next_cell_instance_and_value);
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
static const struct snmp_table_simple_col_def tcp_ConnectionTable_columns[] = {
|
||||
/* all items except tcpConnectionState and tcpConnectionProcess are declared as not-accessible */
|
||||
{ 7, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 }, /* tcpConnectionState */
|
||||
{ 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpConnectionProcess */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node tcp_ConnectionTable = SNMP_TABLE_CREATE_SIMPLE(19, tcp_ConnectionTable_columns, tcp_ConnectionTable_get_cell_value, tcp_ConnectionTable_get_next_cell_instance_and_value);
|
||||
|
||||
|
||||
static const struct snmp_table_simple_col_def tcp_ListenerTable_columns[] = {
|
||||
/* all items except tcpListenerProcess are declared as not-accessible */
|
||||
{ 4, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* tcpListenerProcess */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node tcp_ListenerTable = SNMP_TABLE_CREATE_SIMPLE(20, tcp_ListenerTable_columns, tcp_ListenerTable_get_cell_value, tcp_ListenerTable_get_next_cell_instance_and_value);
|
||||
|
||||
/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
|
||||
CREATE_LWIP_SYNC_NODE( 1, tcp_RtoAlgorithm)
|
||||
CREATE_LWIP_SYNC_NODE( 2, tcp_RtoMin)
|
||||
CREATE_LWIP_SYNC_NODE( 3, tcp_RtoMax)
|
||||
CREATE_LWIP_SYNC_NODE( 4, tcp_MaxConn)
|
||||
CREATE_LWIP_SYNC_NODE( 5, tcp_ActiveOpens)
|
||||
CREATE_LWIP_SYNC_NODE( 6, tcp_PassiveOpens)
|
||||
CREATE_LWIP_SYNC_NODE( 7, tcp_AttemptFails)
|
||||
CREATE_LWIP_SYNC_NODE( 8, tcp_EstabResets)
|
||||
CREATE_LWIP_SYNC_NODE( 9, tcp_CurrEstab)
|
||||
CREATE_LWIP_SYNC_NODE(10, tcp_InSegs)
|
||||
CREATE_LWIP_SYNC_NODE(11, tcp_OutSegs)
|
||||
CREATE_LWIP_SYNC_NODE(12, tcp_RetransSegs)
|
||||
#if LWIP_IPV4
|
||||
CREATE_LWIP_SYNC_NODE(13, tcp_ConnTable)
|
||||
#endif /* LWIP_IPV4 */
|
||||
CREATE_LWIP_SYNC_NODE(14, tcp_InErrs)
|
||||
CREATE_LWIP_SYNC_NODE(15, tcp_OutRsts)
|
||||
CREATE_LWIP_SYNC_NODE(17, tcp_HCInSegs)
|
||||
CREATE_LWIP_SYNC_NODE(18, tcp_HCOutSegs)
|
||||
CREATE_LWIP_SYNC_NODE(19, tcp_ConnectionTable)
|
||||
CREATE_LWIP_SYNC_NODE(20, tcp_ListenerTable)
|
||||
|
||||
static const struct snmp_node* const tcp_nodes[] = {
|
||||
&SYNC_NODE_NAME(tcp_RtoAlgorithm).node.node,
|
||||
&SYNC_NODE_NAME(tcp_RtoMin).node.node,
|
||||
&SYNC_NODE_NAME(tcp_RtoMax).node.node,
|
||||
&SYNC_NODE_NAME(tcp_MaxConn).node.node,
|
||||
&SYNC_NODE_NAME(tcp_ActiveOpens).node.node,
|
||||
&SYNC_NODE_NAME(tcp_PassiveOpens).node.node,
|
||||
&SYNC_NODE_NAME(tcp_AttemptFails).node.node,
|
||||
&SYNC_NODE_NAME(tcp_EstabResets).node.node,
|
||||
&SYNC_NODE_NAME(tcp_CurrEstab).node.node,
|
||||
&SYNC_NODE_NAME(tcp_InSegs).node.node,
|
||||
&SYNC_NODE_NAME(tcp_OutSegs).node.node,
|
||||
&SYNC_NODE_NAME(tcp_RetransSegs).node.node,
|
||||
#if LWIP_IPV4
|
||||
&SYNC_NODE_NAME(tcp_ConnTable).node.node,
|
||||
#endif /* LWIP_IPV4 */
|
||||
&SYNC_NODE_NAME(tcp_InErrs).node.node,
|
||||
&SYNC_NODE_NAME(tcp_OutRsts).node.node,
|
||||
&SYNC_NODE_NAME(tcp_HCInSegs).node.node,
|
||||
&SYNC_NODE_NAME(tcp_HCOutSegs).node.node,
|
||||
&SYNC_NODE_NAME(tcp_ConnectionTable).node.node,
|
||||
&SYNC_NODE_NAME(tcp_ListenerTable).node.node
|
||||
};
|
||||
|
||||
const struct snmp_tree_node snmp_mib2_tcp_root = SNMP_CREATE_TREE_NODE(6, tcp_nodes);
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_TCP */
|
||||
357
src/apps/snmp/snmp_mib2_udp.c
Normal file
357
src/apps/snmp/snmp_mib2_udp.c
Normal file
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
* @file
|
||||
* Management Information Base II (RFC1213) UDP objects and functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*/
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_mib2.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP
|
||||
|
||||
#if SNMP_USE_NETCONN
|
||||
#define SYNC_NODE_NAME(node_name) node_name ## _synced
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name) \
|
||||
static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
|
||||
#else
|
||||
#define SYNC_NODE_NAME(node_name) node_name
|
||||
#define CREATE_LWIP_SYNC_NODE(oid, node_name)
|
||||
#endif
|
||||
|
||||
/* --- udp .1.3.6.1.2.1.7 ----------------------------------------------------- */
|
||||
|
||||
static s16_t
|
||||
udp_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
u32_t *uint_ptr = (u32_t*)value;
|
||||
|
||||
switch (instance->node->oid) {
|
||||
case 1: /* udpInDatagrams */
|
||||
*uint_ptr = STATS_GET(mib2.udpindatagrams);
|
||||
return sizeof(*uint_ptr);
|
||||
case 2: /* udpNoPorts */
|
||||
*uint_ptr = STATS_GET(mib2.udpnoports);
|
||||
return sizeof(*uint_ptr);
|
||||
case 3: /* udpInErrors */
|
||||
*uint_ptr = STATS_GET(mib2.udpinerrors);
|
||||
return sizeof(*uint_ptr);
|
||||
case 4: /* udpOutDatagrams */
|
||||
*uint_ptr = STATS_GET(mib2.udpoutdatagrams);
|
||||
return sizeof(*uint_ptr);
|
||||
case 8: /* udpHCInDatagrams */
|
||||
memset(value, 0, 2*sizeof(u32_t)); /* not supported */
|
||||
return 2*sizeof(u32_t);
|
||||
case 9: /* udpHCOutDatagrams */
|
||||
memset(value, 0, 2*sizeof(u32_t)); /* not supported */
|
||||
return 2*sizeof(u32_t);
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_value(): unknown id: %"S32_F"\n", instance->node->oid));
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --- udpEndpointTable --- */
|
||||
|
||||
static snmp_err_t
|
||||
udp_endpointTable_get_cell_value_core(const u32_t* column, union snmp_variant_value* value)
|
||||
{
|
||||
/* all items except udpEndpointProcess are declared as not-accessible */
|
||||
switch (*column) {
|
||||
case 8: /* udpEndpointProcess */
|
||||
value->u32 = 0; /* not supported */
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
udp_endpointTable_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip_addr_t local_ip, remote_ip;
|
||||
u16_t local_port, remote_port;
|
||||
struct udp_pcb *pcb;
|
||||
u8_t idx = 0;
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
|
||||
idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &local_ip, &local_port);
|
||||
if (idx == 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
|
||||
idx += snmp_oid_to_ip_port(&row_oid[idx], row_oid_len-idx, &remote_ip, &remote_port);
|
||||
if (idx == 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* udpEndpointInstance */
|
||||
if (row_oid_len < (idx+1)) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
if (row_oid[idx] != 0) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* find udp_pcb with requested ip and port*/
|
||||
pcb = udp_pcbs;
|
||||
while (pcb != NULL) {
|
||||
if (ip_addr_cmp(&local_ip, &pcb->local_ip) &&
|
||||
(local_port == pcb->local_port) &&
|
||||
ip_addr_cmp(&remote_ip, &pcb->remote_ip) &&
|
||||
(remote_port == pcb->remote_port)) {
|
||||
/* fill in object properties */
|
||||
return udp_endpointTable_get_cell_value_core(column, value);
|
||||
}
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
udp_endpointTable_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct udp_pcb *pcb;
|
||||
struct snmp_next_oid_state state;
|
||||
/* 1x udpEndpointLocalAddressType + 1x OID len + 16x udpEndpointLocalAddress + 1x udpEndpointLocalPort +
|
||||
* 1x udpEndpointRemoteAddressType + 1x OID len + 16x udpEndpointRemoteAddress + 1x udpEndpointRemotePort +
|
||||
* 1x udpEndpointInstance = 39
|
||||
*/
|
||||
u32_t result_temp[39];
|
||||
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(result_temp));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
pcb = udp_pcbs;
|
||||
while (pcb != NULL) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(result_temp)];
|
||||
u8_t idx = 0;
|
||||
|
||||
/* udpEndpointLocalAddressType + udpEndpointLocalAddress + udpEndpointLocalPort */
|
||||
idx += snmp_ip_port_to_oid(&pcb->local_ip, pcb->local_port, &test_oid[idx]);
|
||||
|
||||
/* udpEndpointRemoteAddressType + udpEndpointRemoteAddress + udpEndpointRemotePort */
|
||||
idx += snmp_ip_port_to_oid(&pcb->remote_ip, pcb->remote_port, &test_oid[idx]);
|
||||
|
||||
test_oid[idx] = 0; /* udpEndpointInstance */
|
||||
idx++;
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, idx, NULL);
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return udp_endpointTable_get_cell_value_core(column, value);
|
||||
} else {
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- udpTable --- */
|
||||
|
||||
#if LWIP_IPV4
|
||||
|
||||
/* list of allowed value ranges for incoming OID */
|
||||
static const struct snmp_oid_range udp_Table_oid_ranges[] = {
|
||||
{ 0, 0xff }, /* IP A */
|
||||
{ 0, 0xff }, /* IP B */
|
||||
{ 0, 0xff }, /* IP C */
|
||||
{ 0, 0xff }, /* IP D */
|
||||
{ 1, 0xffff } /* Port */
|
||||
};
|
||||
|
||||
static snmp_err_t
|
||||
udp_Table_get_cell_value_core(struct udp_pcb *pcb, const u32_t* column, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
LWIP_UNUSED_ARG(value_len);
|
||||
|
||||
switch (*column) {
|
||||
case 1: /* udpLocalAddress */
|
||||
/* set reference to PCB local IP and return a generic node that copies IP4 addresses */
|
||||
value->u32 = ip_2_ip4(&pcb->local_ip)->addr;
|
||||
break;
|
||||
case 2: /* udpLocalPort */
|
||||
/* set reference to PCB local port and return a generic node that copies u16_t values */
|
||||
value->u32 = pcb->local_port;
|
||||
break;
|
||||
default:
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
udp_Table_get_cell_value(const u32_t* column, const u32_t* row_oid, u8_t row_oid_len, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
ip4_addr_t ip;
|
||||
u16_t port;
|
||||
struct udp_pcb *pcb;
|
||||
|
||||
/* check if incoming OID length and if values are in plausible range */
|
||||
if (!snmp_oid_in_range(row_oid, row_oid_len, udp_Table_oid_ranges, LWIP_ARRAYSIZE(udp_Table_oid_ranges))) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
/* get IP and port from incoming OID */
|
||||
snmp_oid_to_ip4(&row_oid[0], &ip); /* we know it succeeds because of oid_in_range check above */
|
||||
port = (u16_t)row_oid[4];
|
||||
|
||||
/* find udp_pcb with requested ip and port*/
|
||||
pcb = udp_pcbs;
|
||||
while (pcb != NULL) {
|
||||
if (IP_IS_V4_VAL(pcb->local_ip)) {
|
||||
if (ip4_addr_cmp(&ip, ip_2_ip4(&pcb->local_ip)) && (port == pcb->local_port)) {
|
||||
/* fill in object properties */
|
||||
return udp_Table_get_cell_value_core(pcb, column, value, value_len);
|
||||
}
|
||||
}
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
udp_Table_get_next_cell_instance_and_value(const u32_t* column, struct snmp_obj_id* row_oid, union snmp_variant_value* value, u32_t* value_len)
|
||||
{
|
||||
struct udp_pcb *pcb;
|
||||
struct snmp_next_oid_state state;
|
||||
u32_t result_temp[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
|
||||
|
||||
/* init struct to search next oid */
|
||||
snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(udp_Table_oid_ranges));
|
||||
|
||||
/* iterate over all possible OIDs to find the next one */
|
||||
pcb = udp_pcbs;
|
||||
while (pcb != NULL) {
|
||||
u32_t test_oid[LWIP_ARRAYSIZE(udp_Table_oid_ranges)];
|
||||
|
||||
if (IP_IS_V4_VAL(pcb->local_ip)) {
|
||||
snmp_ip4_to_oid(ip_2_ip4(&pcb->local_ip), &test_oid[0]);
|
||||
test_oid[4] = pcb->local_port;
|
||||
|
||||
/* check generated OID: is it a candidate for the next one? */
|
||||
snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(udp_Table_oid_ranges), pcb);
|
||||
}
|
||||
|
||||
pcb = pcb->next;
|
||||
}
|
||||
|
||||
/* did we find a next one? */
|
||||
if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
|
||||
snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
|
||||
/* fill in object properties */
|
||||
return udp_Table_get_cell_value_core((struct udp_pcb*)state.reference, column, value, value_len);
|
||||
} else {
|
||||
/* not found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
static const struct snmp_scalar_node udp_inDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
|
||||
static const struct snmp_scalar_node udp_noPorts = SNMP_SCALAR_CREATE_NODE_READONLY(2, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
|
||||
static const struct snmp_scalar_node udp_inErrors = SNMP_SCALAR_CREATE_NODE_READONLY(3, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
|
||||
static const struct snmp_scalar_node udp_outDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(4, SNMP_ASN1_TYPE_COUNTER, udp_get_value);
|
||||
static const struct snmp_scalar_node udp_HCInDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(8, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
|
||||
static const struct snmp_scalar_node udp_HCOutDatagrams = SNMP_SCALAR_CREATE_NODE_READONLY(9, SNMP_ASN1_TYPE_COUNTER64, udp_get_value);
|
||||
|
||||
#if LWIP_IPV4
|
||||
static const struct snmp_table_simple_col_def udp_Table_columns[] = {
|
||||
{ 1, SNMP_ASN1_TYPE_IPADDR, SNMP_VARIANT_VALUE_TYPE_U32 }, /* udpLocalAddress */
|
||||
{ 2, SNMP_ASN1_TYPE_INTEGER, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpLocalPort */
|
||||
};
|
||||
static const struct snmp_table_simple_node udp_Table = SNMP_TABLE_CREATE_SIMPLE(5, udp_Table_columns, udp_Table_get_cell_value, udp_Table_get_next_cell_instance_and_value);
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
static const struct snmp_table_simple_col_def udp_endpointTable_columns[] = {
|
||||
/* all items except udpEndpointProcess are declared as not-accessible */
|
||||
{ 8, SNMP_ASN1_TYPE_UNSIGNED32, SNMP_VARIANT_VALUE_TYPE_U32 } /* udpEndpointProcess */
|
||||
};
|
||||
|
||||
static const struct snmp_table_simple_node udp_endpointTable = SNMP_TABLE_CREATE_SIMPLE(7, udp_endpointTable_columns, udp_endpointTable_get_cell_value, udp_endpointTable_get_next_cell_instance_and_value);
|
||||
|
||||
/* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
|
||||
CREATE_LWIP_SYNC_NODE(1, udp_inDatagrams)
|
||||
CREATE_LWIP_SYNC_NODE(2, udp_noPorts)
|
||||
CREATE_LWIP_SYNC_NODE(3, udp_inErrors)
|
||||
CREATE_LWIP_SYNC_NODE(4, udp_outDatagrams)
|
||||
#if LWIP_IPV4
|
||||
CREATE_LWIP_SYNC_NODE(5, udp_Table)
|
||||
#endif /* LWIP_IPV4 */
|
||||
CREATE_LWIP_SYNC_NODE(7, udp_endpointTable)
|
||||
CREATE_LWIP_SYNC_NODE(8, udp_HCInDatagrams)
|
||||
CREATE_LWIP_SYNC_NODE(9, udp_HCOutDatagrams)
|
||||
|
||||
static const struct snmp_node* const udp_nodes[] = {
|
||||
&SYNC_NODE_NAME(udp_inDatagrams).node.node,
|
||||
&SYNC_NODE_NAME(udp_noPorts).node.node,
|
||||
&SYNC_NODE_NAME(udp_inErrors).node.node,
|
||||
&SYNC_NODE_NAME(udp_outDatagrams).node.node,
|
||||
#if LWIP_IPV4
|
||||
&SYNC_NODE_NAME(udp_Table).node.node,
|
||||
#endif /* LWIP_IPV4 */
|
||||
&SYNC_NODE_NAME(udp_endpointTable).node.node,
|
||||
&SYNC_NODE_NAME(udp_HCInDatagrams).node.node,
|
||||
&SYNC_NODE_NAME(udp_HCOutDatagrams).node.node
|
||||
};
|
||||
|
||||
const struct snmp_tree_node snmp_mib2_udp_root = SNMP_CREATE_TREE_NODE(7, udp_nodes);
|
||||
#endif /* LWIP_SNMP && SNMP_LWIP_MIB2 && LWIP_UDP */
|
||||
1668
src/apps/snmp/snmp_msg.c
Normal file
1668
src/apps/snmp/snmp_msg.c
Normal file
File diff suppressed because it is too large
Load Diff
194
src/apps/snmp/snmp_msg.h
Normal file
194
src/apps/snmp/snmp_msg.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMP Agent message handling structures (internal API, do not use in client code).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
|
||||
* Copyright (c) 2016 Elias Oenal.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Christiaan Simons <christiaan.simons@axon.tv>
|
||||
* Martin Hentschel <info@cl-soft.de>
|
||||
* Elias Oenal <lwip@eliasoenal.com>
|
||||
*/
|
||||
|
||||
#ifndef LWIP_HDR_APPS_SNMP_MSG_H
|
||||
#define LWIP_HDR_APPS_SNMP_MSG_H
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP
|
||||
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "snmp_pbuf_stream.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/err.h"
|
||||
|
||||
#if LWIP_SNMP_V3
|
||||
#include "snmpv3_priv.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* The listen port of the SNMP agent. Clients have to make their requests to
|
||||
this port. Most standard clients won't work if you change this! */
|
||||
#ifndef SNMP_IN_PORT
|
||||
#define SNMP_IN_PORT 161
|
||||
#endif
|
||||
/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't
|
||||
work if you change this! */
|
||||
#ifndef SNMP_TRAP_PORT
|
||||
#define SNMP_TRAP_PORT 162
|
||||
#endif
|
||||
|
||||
/* version defines used in PDU */
|
||||
#define SNMP_VERSION_1 0
|
||||
#define SNMP_VERSION_2c 1
|
||||
#define SNMP_VERSION_3 3
|
||||
|
||||
struct snmp_varbind_enumerator
|
||||
{
|
||||
struct snmp_pbuf_stream pbuf_stream;
|
||||
u16_t varbind_count;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SNMP_VB_ENUMERATOR_ERR_OK = 0,
|
||||
SNMP_VB_ENUMERATOR_ERR_EOVB = 1,
|
||||
SNMP_VB_ENUMERATOR_ERR_ASN1ERROR = 2,
|
||||
SNMP_VB_ENUMERATOR_ERR_INVALIDLENGTH = 3
|
||||
} snmp_vb_enumerator_err_t;
|
||||
|
||||
void snmp_vb_enumerator_init(struct snmp_varbind_enumerator* enumerator, struct pbuf* p, u16_t offset, u16_t length);
|
||||
snmp_vb_enumerator_err_t snmp_vb_enumerator_get_next(struct snmp_varbind_enumerator* enumerator, struct snmp_varbind* varbind);
|
||||
|
||||
struct snmp_request
|
||||
{
|
||||
/* Communication handle */
|
||||
void *handle;
|
||||
/* source IP address */
|
||||
const ip_addr_t *source_ip;
|
||||
/* source UDP port */
|
||||
u16_t source_port;
|
||||
/* incoming snmp version */
|
||||
u8_t version;
|
||||
/* community name (zero terminated) */
|
||||
u8_t community[SNMP_MAX_COMMUNITY_STR_LEN + 1];
|
||||
/* community string length (exclusive zero term) */
|
||||
u16_t community_strlen;
|
||||
/* request type */
|
||||
u8_t request_type;
|
||||
/* request ID */
|
||||
s32_t request_id;
|
||||
/* error status */
|
||||
s32_t error_status;
|
||||
/* error index */
|
||||
s32_t error_index;
|
||||
/* non-repeaters (getBulkRequest (SNMPv2c)) */
|
||||
s32_t non_repeaters;
|
||||
/* max-repetitions (getBulkRequest (SNMPv2c)) */
|
||||
s32_t max_repetitions;
|
||||
|
||||
#if LWIP_SNMP_V3
|
||||
s32_t msg_id;
|
||||
s32_t msg_max_size;
|
||||
u8_t msg_flags;
|
||||
s32_t msg_security_model;
|
||||
u8_t msg_authoritative_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
|
||||
u8_t msg_authoritative_engine_id_len;
|
||||
s32_t msg_authoritative_engine_boots;
|
||||
s32_t msg_authoritative_engine_time;
|
||||
u8_t msg_user_name[SNMP_V3_MAX_USER_LENGTH];
|
||||
u8_t msg_user_name_len;
|
||||
u8_t msg_authentication_parameters[SNMP_V3_MAX_AUTH_PARAM_LENGTH];
|
||||
u8_t msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH];
|
||||
u8_t context_engine_id[SNMP_V3_MAX_ENGINE_ID_LENGTH];
|
||||
u8_t context_engine_id_len;
|
||||
u8_t context_name[SNMP_V3_MAX_ENGINE_ID_LENGTH];
|
||||
u8_t context_name_len;
|
||||
#endif
|
||||
|
||||
struct pbuf *inbound_pbuf;
|
||||
struct snmp_varbind_enumerator inbound_varbind_enumerator;
|
||||
u16_t inbound_varbind_offset;
|
||||
u16_t inbound_varbind_len;
|
||||
u16_t inbound_padding_len;
|
||||
|
||||
struct pbuf *outbound_pbuf;
|
||||
struct snmp_pbuf_stream outbound_pbuf_stream;
|
||||
u16_t outbound_pdu_offset;
|
||||
u16_t outbound_error_status_offset;
|
||||
u16_t outbound_error_index_offset;
|
||||
u16_t outbound_varbind_offset;
|
||||
#if LWIP_SNMP_V3
|
||||
u16_t outbound_msg_global_data_offset;
|
||||
u16_t outbound_msg_global_data_end;
|
||||
u16_t outbound_msg_security_parameters_str_offset;
|
||||
u16_t outbound_msg_security_parameters_seq_offset;
|
||||
u16_t outbound_msg_security_parameters_end;
|
||||
u16_t outbound_msg_authentication_parameters_offset;
|
||||
u16_t outbound_scoped_pdu_seq_offset;
|
||||
u16_t outbound_scoped_pdu_string_offset;
|
||||
#endif
|
||||
|
||||
u8_t value_buffer[SNMP_MAX_VALUE_SIZE];
|
||||
};
|
||||
|
||||
/** A helper struct keeping length information about varbinds */
|
||||
struct snmp_varbind_len
|
||||
{
|
||||
u8_t vb_len_len;
|
||||
u16_t vb_value_len;
|
||||
u8_t oid_len_len;
|
||||
u16_t oid_value_len;
|
||||
u8_t value_len_len;
|
||||
u16_t value_value_len;
|
||||
};
|
||||
|
||||
/** Agent community string */
|
||||
extern const char *snmp_community;
|
||||
/** Agent community string for write access */
|
||||
extern const char *snmp_community_write;
|
||||
/** handle for sending traps */
|
||||
extern void* snmp_traps_handle;
|
||||
|
||||
void snmp_receive(void *handle, struct pbuf *p, const ip_addr_t *source_ip, u16_t port);
|
||||
err_t snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port);
|
||||
u8_t snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result);
|
||||
err_t snmp_varbind_length(struct snmp_varbind *varbind, struct snmp_varbind_len *len);
|
||||
err_t snmp_append_outbound_varbind(struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind* varbind);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
|
||||
#endif /* LWIP_HDR_APPS_SNMP_MSG_H */
|
||||
120
src/apps/snmp/snmp_netconn.c
Normal file
120
src/apps/snmp/snmp_netconn.c
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMP netconn frontend.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP && SNMP_USE_NETCONN
|
||||
|
||||
#include "lwip/api.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "snmp_msg.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
/** SNMP netconn API worker thread */
|
||||
static void
|
||||
snmp_netconn_thread(void *arg)
|
||||
{
|
||||
struct netconn *conn;
|
||||
struct netbuf *buf;
|
||||
err_t err;
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
|
||||
/* Bind to SNMP port with default IP address */
|
||||
#if LWIP_IPV6
|
||||
conn = netconn_new(NETCONN_UDP_IPV6);
|
||||
netconn_bind(conn, IP6_ADDR_ANY, SNMP_IN_PORT);
|
||||
#else /* LWIP_IPV6 */
|
||||
conn = netconn_new(NETCONN_UDP);
|
||||
netconn_bind(conn, IP4_ADDR_ANY, SNMP_IN_PORT);
|
||||
#endif /* LWIP_IPV6 */
|
||||
LWIP_ERROR("snmp_netconn: invalid conn", (conn != NULL), return;);
|
||||
|
||||
snmp_traps_handle = conn;
|
||||
|
||||
do {
|
||||
err = netconn_recv(conn, &buf);
|
||||
|
||||
if (err == ERR_OK) {
|
||||
snmp_receive(conn, buf->p, &buf->addr, buf->port);
|
||||
}
|
||||
|
||||
if (buf != NULL) {
|
||||
netbuf_delete(buf);
|
||||
}
|
||||
} while(1);
|
||||
}
|
||||
|
||||
err_t
|
||||
snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
|
||||
{
|
||||
err_t result;
|
||||
struct netbuf buf;
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
buf.p = p;
|
||||
result = netconn_sendto((struct netconn*)handle, &buf, dst, port);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
u8_t
|
||||
snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result)
|
||||
{
|
||||
struct netconn* conn = (struct netconn*)handle;
|
||||
struct netif *dst_if;
|
||||
const ip_addr_t* dst_ip;
|
||||
|
||||
LWIP_UNUSED_ARG(conn); /* unused in case of IPV4 only configuration */
|
||||
|
||||
ip_route_get_local_ip(&conn->pcb.udp->local_ip, dst, dst_if, dst_ip);
|
||||
|
||||
if ((dst_if != NULL) && (dst_ip != NULL)) {
|
||||
ip_addr_copy(*result, *dst_ip);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts SNMP Agent.
|
||||
*/
|
||||
void
|
||||
snmp_init(void)
|
||||
{
|
||||
sys_thread_new("snmp_netconn", snmp_netconn_thread, NULL, SNMP_STACK_SIZE, SNMP_THREAD_PRIO);
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_USE_NETCONN */
|
||||
156
src/apps/snmp/snmp_pbuf_stream.c
Normal file
156
src/apps/snmp/snmp_pbuf_stream.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMP pbuf stream wrapper implementation (internal API, do not use in client code).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Martin Hentschel <info@cl-soft.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "snmp_pbuf_stream.h"
|
||||
#include "lwip/def.h"
|
||||
#include <string.h>
|
||||
|
||||
err_t
|
||||
snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length)
|
||||
{
|
||||
pbuf_stream->offset = offset;
|
||||
pbuf_stream->length = length;
|
||||
pbuf_stream->pbuf = p;
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t
|
||||
snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data)
|
||||
{
|
||||
if (pbuf_stream->length == 0) {
|
||||
return ERR_BUF;
|
||||
}
|
||||
|
||||
if (pbuf_copy_partial(pbuf_stream->pbuf, data, 1, pbuf_stream->offset) == 0) {
|
||||
return ERR_BUF;
|
||||
}
|
||||
|
||||
pbuf_stream->offset++;
|
||||
pbuf_stream->length--;
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t
|
||||
snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data)
|
||||
{
|
||||
return snmp_pbuf_stream_writebuf(pbuf_stream, &data, 1);
|
||||
}
|
||||
|
||||
err_t
|
||||
snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len)
|
||||
{
|
||||
if (pbuf_stream->length < buf_len) {
|
||||
return ERR_BUF;
|
||||
}
|
||||
|
||||
if (pbuf_take_at(pbuf_stream->pbuf, buf, buf_len, pbuf_stream->offset) != ERR_OK) {
|
||||
return ERR_BUF;
|
||||
}
|
||||
|
||||
pbuf_stream->offset += buf_len;
|
||||
pbuf_stream->length -= buf_len;
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t
|
||||
snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len)
|
||||
{
|
||||
|
||||
if ((pbuf_stream == NULL) || (target_pbuf_stream == NULL)) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if ((len > pbuf_stream->length) || (len > target_pbuf_stream->length)) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
len = LWIP_MIN(pbuf_stream->length, target_pbuf_stream->length);
|
||||
}
|
||||
|
||||
while (len > 0) {
|
||||
u16_t chunk_len;
|
||||
err_t err;
|
||||
u16_t target_offset;
|
||||
struct pbuf* pbuf = pbuf_skip(pbuf_stream->pbuf, pbuf_stream->offset, &target_offset);
|
||||
|
||||
if ((pbuf == NULL) || (pbuf->len == 0)) {
|
||||
return ERR_BUF;
|
||||
}
|
||||
|
||||
chunk_len = LWIP_MIN(len, pbuf->len);
|
||||
err = snmp_pbuf_stream_writebuf(target_pbuf_stream, &((u8_t*)pbuf->payload)[target_offset], chunk_len);
|
||||
if (err != ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
pbuf_stream->offset += chunk_len;
|
||||
pbuf_stream->length -= chunk_len;
|
||||
len -= chunk_len;
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t
|
||||
snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset)
|
||||
{
|
||||
if ((offset < 0) || (offset > pbuf_stream->length)) {
|
||||
/* we cannot seek backwards or forward behind stream end */
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
pbuf_stream->offset += (u16_t)offset;
|
||||
pbuf_stream->length -= (u16_t)offset;
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
err_t
|
||||
snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset)
|
||||
{
|
||||
s32_t rel_offset = offset - pbuf_stream->offset;
|
||||
return snmp_pbuf_stream_seek(pbuf_stream, rel_offset);
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
73
src/apps/snmp/snmp_pbuf_stream.h
Normal file
73
src/apps/snmp/snmp_pbuf_stream.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMP pbuf stream wrapper (internal API, do not use in client code).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Martin Hentschel <info@cl-soft.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
|
||||
#define LWIP_HDR_APPS_SNMP_PBUF_STREAM_H
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/pbuf.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct snmp_pbuf_stream
|
||||
{
|
||||
struct pbuf* pbuf;
|
||||
u16_t offset;
|
||||
u16_t length;
|
||||
};
|
||||
|
||||
err_t snmp_pbuf_stream_init(struct snmp_pbuf_stream* pbuf_stream, struct pbuf* p, u16_t offset, u16_t length);
|
||||
err_t snmp_pbuf_stream_read(struct snmp_pbuf_stream* pbuf_stream, u8_t* data);
|
||||
err_t snmp_pbuf_stream_write(struct snmp_pbuf_stream* pbuf_stream, u8_t data);
|
||||
err_t snmp_pbuf_stream_writebuf(struct snmp_pbuf_stream* pbuf_stream, const void* buf, u16_t buf_len);
|
||||
err_t snmp_pbuf_stream_writeto(struct snmp_pbuf_stream* pbuf_stream, struct snmp_pbuf_stream* target_pbuf_stream, u16_t len);
|
||||
err_t snmp_pbuf_stream_seek(struct snmp_pbuf_stream* pbuf_stream, s32_t offset);
|
||||
err_t snmp_pbuf_stream_seek_abs(struct snmp_pbuf_stream* pbuf_stream, u32_t offset);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
|
||||
#endif /* LWIP_HDR_APPS_SNMP_PBUF_STREAM_H */
|
||||
100
src/apps/snmp/snmp_raw.c
Normal file
100
src/apps/snmp/snmp_raw.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMP RAW API frontend.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
|
||||
#if LWIP_SNMP && SNMP_USE_RAW
|
||||
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "snmp_msg.h"
|
||||
|
||||
/* lwIP UDP receive callback function */
|
||||
static void
|
||||
snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
|
||||
{
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
|
||||
snmp_receive(pcb, p, addr, port);
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
err_t
|
||||
snmp_sendto(void *handle, struct pbuf *p, const ip_addr_t *dst, u16_t port)
|
||||
{
|
||||
return udp_sendto((struct udp_pcb*)handle, p, dst, port);
|
||||
}
|
||||
|
||||
u8_t
|
||||
snmp_get_local_ip_for_dst(void* handle, const ip_addr_t *dst, ip_addr_t *result)
|
||||
{
|
||||
struct udp_pcb* udp_pcb = (struct udp_pcb*)handle;
|
||||
struct netif *dst_if;
|
||||
const ip_addr_t* dst_ip;
|
||||
|
||||
LWIP_UNUSED_ARG(udp_pcb); /* unused in case of IPV4 only configuration */
|
||||
|
||||
ip_route_get_local_ip(&udp_pcb->local_ip, dst, dst_if, dst_ip);
|
||||
|
||||
if ((dst_if != NULL) && (dst_ip != NULL)) {
|
||||
ip_addr_copy(*result, *dst_ip);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_core
|
||||
* Starts SNMP Agent.
|
||||
* Allocates UDP pcb and binds it to IP_ANY_TYPE port 161.
|
||||
*/
|
||||
void
|
||||
snmp_init(void)
|
||||
{
|
||||
err_t err;
|
||||
|
||||
struct udp_pcb *snmp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
|
||||
LWIP_ERROR("snmp_raw: no PCB", (snmp_pcb != NULL), return;);
|
||||
|
||||
snmp_traps_handle = snmp_pcb;
|
||||
|
||||
udp_recv(snmp_pcb, snmp_recv, (void *)SNMP_IN_PORT);
|
||||
err = udp_bind(snmp_pcb, IP_ANY_TYPE, SNMP_IN_PORT);
|
||||
LWIP_ERROR("snmp_raw: Unable to bind PCB", (err == ERR_OK), return;);
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP && SNMP_USE_RAW */
|
||||
220
src/apps/snmp/snmp_scalar.c
Normal file
220
src/apps/snmp/snmp_scalar.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMP scalar node support implementation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Martin Hentschel <info@cl-soft.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/apps/snmp_scalar.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
|
||||
static s16_t snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value);
|
||||
static snmp_err_t snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value);
|
||||
static snmp_err_t snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value);
|
||||
|
||||
snmp_err_t
|
||||
snmp_scalar_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
|
||||
{
|
||||
const struct snmp_scalar_node* scalar_node = (const struct snmp_scalar_node*)(const void*)instance->node;
|
||||
|
||||
LWIP_UNUSED_ARG(root_oid);
|
||||
LWIP_UNUSED_ARG(root_oid_len);
|
||||
|
||||
/* scalar only has one dedicated instance: .0 */
|
||||
if ((instance->instance_oid.len != 1) || (instance->instance_oid.id[0] != 0)) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
instance->access = scalar_node->access;
|
||||
instance->asn1_type = scalar_node->asn1_type;
|
||||
instance->get_value = scalar_node->get_value;
|
||||
instance->set_test = scalar_node->set_test;
|
||||
instance->set_value = scalar_node->set_value;
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
snmp_err_t
|
||||
snmp_scalar_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
|
||||
{
|
||||
/* because our only instance is .0 we can only return a next instance if no instance oid is passed */
|
||||
if (instance->instance_oid.len == 0) {
|
||||
instance->instance_oid.len = 1;
|
||||
instance->instance_oid.id[0] = 0;
|
||||
|
||||
return snmp_scalar_get_instance(root_oid, root_oid_len, instance);
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
|
||||
snmp_err_t
|
||||
snmp_scalar_array_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
|
||||
{
|
||||
LWIP_UNUSED_ARG(root_oid);
|
||||
LWIP_UNUSED_ARG(root_oid_len);
|
||||
|
||||
if ((instance->instance_oid.len == 2) && (instance->instance_oid.id[1] == 0)) {
|
||||
const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
|
||||
const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes;
|
||||
u32_t i = 0;
|
||||
|
||||
while (i < array_node->array_node_count) {
|
||||
if (array_node_def->oid == instance->instance_oid.id[0]) {
|
||||
break;
|
||||
}
|
||||
|
||||
array_node_def++;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i < array_node->array_node_count) {
|
||||
instance->access = array_node_def->access;
|
||||
instance->asn1_type = array_node_def->asn1_type;
|
||||
instance->get_value = snmp_scalar_array_get_value;
|
||||
instance->set_test = snmp_scalar_array_set_test;
|
||||
instance->set_value = snmp_scalar_array_set_value;
|
||||
instance->reference.const_ptr = array_node_def;
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
snmp_err_t
|
||||
snmp_scalar_array_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
|
||||
{
|
||||
const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
|
||||
const struct snmp_scalar_array_node_def* array_node_def = array_node->array_nodes;
|
||||
const struct snmp_scalar_array_node_def* result = NULL;
|
||||
|
||||
LWIP_UNUSED_ARG(root_oid);
|
||||
LWIP_UNUSED_ARG(root_oid_len);
|
||||
|
||||
if ((instance->instance_oid.len == 0) && (array_node->array_node_count > 0)) {
|
||||
/* return node with lowest OID */
|
||||
u16_t i = 0;
|
||||
|
||||
result = array_node_def;
|
||||
array_node_def++;
|
||||
|
||||
for (i = 1; i < array_node->array_node_count; i++) {
|
||||
if (array_node_def->oid < result->oid) {
|
||||
result = array_node_def;
|
||||
}
|
||||
array_node_def++;
|
||||
}
|
||||
} else if (instance->instance_oid.len >= 1) {
|
||||
if (instance->instance_oid.len == 1) {
|
||||
/* if we have the requested OID we return its instance, otherwise we search for the next available */
|
||||
u16_t i = 0;
|
||||
while (i < array_node->array_node_count) {
|
||||
if (array_node_def->oid == instance->instance_oid.id[0]) {
|
||||
result = array_node_def;
|
||||
break;
|
||||
}
|
||||
|
||||
array_node_def++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (result == NULL) {
|
||||
u32_t oid_dist = 0xFFFFFFFFUL;
|
||||
u16_t i = 0;
|
||||
array_node_def = array_node->array_nodes; /* may be already at the end when if case before was executed without result -> reinitialize to start */
|
||||
while (i < array_node->array_node_count) {
|
||||
if ((array_node_def->oid > instance->instance_oid.id[0]) &&
|
||||
((u32_t)(array_node_def->oid - instance->instance_oid.id[0]) < oid_dist)) {
|
||||
result = array_node_def;
|
||||
oid_dist = array_node_def->oid - instance->instance_oid.id[0];
|
||||
}
|
||||
|
||||
array_node_def++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == NULL) {
|
||||
/* nothing to return */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
instance->instance_oid.len = 2;
|
||||
instance->instance_oid.id[0] = result->oid;
|
||||
instance->instance_oid.id[1] = 0;
|
||||
|
||||
instance->access = result->access;
|
||||
instance->asn1_type = result->asn1_type;
|
||||
instance->get_value = snmp_scalar_array_get_value;
|
||||
instance->set_test = snmp_scalar_array_set_test;
|
||||
instance->set_value = snmp_scalar_array_set_value;
|
||||
instance->reference.const_ptr = result;
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
static s16_t
|
||||
snmp_scalar_array_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
|
||||
const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
|
||||
|
||||
return array_node->get_value(array_node_def, value);
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
snmp_scalar_array_set_test(struct snmp_node_instance* instance, u16_t value_len, void* value)
|
||||
{
|
||||
const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
|
||||
const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
|
||||
|
||||
return array_node->set_test(array_node_def, value_len, value);
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
snmp_scalar_array_set_value(struct snmp_node_instance* instance, u16_t value_len, void* value)
|
||||
{
|
||||
const struct snmp_scalar_array_node* array_node = (const struct snmp_scalar_array_node*)(const void*)instance->node;
|
||||
const struct snmp_scalar_array_node_def* array_node_def = (const struct snmp_scalar_array_node_def*)instance->reference.const_ptr;
|
||||
|
||||
return array_node->set_value(array_node_def, value_len, value);
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
343
src/apps/snmp/snmp_table.c
Normal file
343
src/apps/snmp/snmp_table.c
Normal file
@@ -0,0 +1,343 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMP table support implementation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Martin Hentschel <info@cl-soft.de>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/apps/snmp_table.h"
|
||||
#include <string.h>
|
||||
|
||||
snmp_err_t snmp_table_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
|
||||
{
|
||||
snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
|
||||
const struct snmp_table_node* table_node = (const struct snmp_table_node*)(const void*)instance->node;
|
||||
|
||||
LWIP_UNUSED_ARG(root_oid);
|
||||
LWIP_UNUSED_ARG(root_oid_len);
|
||||
|
||||
/* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
|
||||
/* fixed row entry always has oid 1 */
|
||||
if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
|
||||
/* search column */
|
||||
const struct snmp_table_col_def* col_def = table_node->columns;
|
||||
u16_t i = table_node->column_count;
|
||||
while (i > 0) {
|
||||
if (col_def->index == instance->instance_oid.id[1]) {
|
||||
break;
|
||||
}
|
||||
|
||||
col_def++;
|
||||
i--;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
/* everything may be overwritten by get_cell_instance_method() in order to implement special handling for single columns/cells */
|
||||
instance->asn1_type = col_def->asn1_type;
|
||||
instance->access = col_def->access;
|
||||
instance->get_value = table_node->get_value;
|
||||
instance->set_test = table_node->set_test;
|
||||
instance->set_value = table_node->set_value;
|
||||
|
||||
ret = table_node->get_cell_instance(
|
||||
&(instance->instance_oid.id[1]),
|
||||
&(instance->instance_oid.id[2]),
|
||||
instance->instance_oid.len-2,
|
||||
instance);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
snmp_err_t snmp_table_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
|
||||
{
|
||||
const struct snmp_table_node* table_node = (const struct snmp_table_node*)(const void*)instance->node;
|
||||
const struct snmp_table_col_def* col_def;
|
||||
struct snmp_obj_id row_oid;
|
||||
u32_t column = 0;
|
||||
snmp_err_t result;
|
||||
|
||||
LWIP_UNUSED_ARG(root_oid);
|
||||
LWIP_UNUSED_ARG(root_oid_len);
|
||||
|
||||
/* check that first part of id is 0 or 1, referencing fixed row entry */
|
||||
if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
if (instance->instance_oid.len > 1) {
|
||||
column = instance->instance_oid.id[1];
|
||||
}
|
||||
if (instance->instance_oid.len > 2) {
|
||||
snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
|
||||
} else {
|
||||
row_oid.len = 0;
|
||||
}
|
||||
|
||||
instance->get_value = table_node->get_value;
|
||||
instance->set_test = table_node->set_test;
|
||||
instance->set_value = table_node->set_value;
|
||||
|
||||
/* resolve column and value */
|
||||
do {
|
||||
u16_t i;
|
||||
const struct snmp_table_col_def* next_col_def = NULL;
|
||||
col_def = table_node->columns;
|
||||
|
||||
for (i = 0; i < table_node->column_count; i++) {
|
||||
if (col_def->index == column) {
|
||||
next_col_def = col_def;
|
||||
break;
|
||||
} else if ((col_def->index > column) && ((next_col_def == NULL) || (col_def->index < next_col_def->index))) {
|
||||
next_col_def = col_def;
|
||||
}
|
||||
col_def++;
|
||||
}
|
||||
|
||||
if (next_col_def == NULL) {
|
||||
/* no further column found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
instance->asn1_type = next_col_def->asn1_type;
|
||||
instance->access = next_col_def->access;
|
||||
|
||||
result = table_node->get_next_cell_instance(
|
||||
&next_col_def->index,
|
||||
&row_oid,
|
||||
instance);
|
||||
|
||||
if (result == SNMP_ERR_NOERROR) {
|
||||
col_def = next_col_def;
|
||||
break;
|
||||
}
|
||||
|
||||
row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
|
||||
column = next_col_def->index + 1;
|
||||
} while (1);
|
||||
|
||||
/* build resulting oid */
|
||||
instance->instance_oid.len = 2;
|
||||
instance->instance_oid.id[0] = 1;
|
||||
instance->instance_oid.id[1] = col_def->index;
|
||||
snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
|
||||
snmp_err_t snmp_table_simple_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
|
||||
{
|
||||
snmp_err_t ret = SNMP_ERR_NOSUCHINSTANCE;
|
||||
const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)(const void*)instance->node;
|
||||
|
||||
LWIP_UNUSED_ARG(root_oid);
|
||||
LWIP_UNUSED_ARG(root_oid_len);
|
||||
|
||||
/* check min. length (fixed row entry definition, column, row instance oid with at least one entry */
|
||||
/* fixed row entry always has oid 1 */
|
||||
if ((instance->instance_oid.len >= 3) && (instance->instance_oid.id[0] == 1)) {
|
||||
ret = table_node->get_cell_value(
|
||||
&(instance->instance_oid.id[1]),
|
||||
&(instance->instance_oid.id[2]),
|
||||
instance->instance_oid.len-2,
|
||||
&instance->reference,
|
||||
&instance->reference_len);
|
||||
|
||||
if (ret == SNMP_ERR_NOERROR) {
|
||||
/* search column */
|
||||
const struct snmp_table_simple_col_def* col_def = table_node->columns;
|
||||
u32_t i = table_node->column_count;
|
||||
while (i > 0) {
|
||||
if (col_def->index == instance->instance_oid.id[1]) {
|
||||
break;
|
||||
}
|
||||
|
||||
col_def++;
|
||||
i--;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
instance->asn1_type = col_def->asn1_type;
|
||||
instance->access = SNMP_NODE_INSTANCE_READ_ONLY;
|
||||
instance->set_test = NULL;
|
||||
instance->set_value = NULL;
|
||||
|
||||
switch (col_def->data_type) {
|
||||
case SNMP_VARIANT_VALUE_TYPE_U32:
|
||||
instance->get_value = snmp_table_extract_value_from_u32ref;
|
||||
break;
|
||||
case SNMP_VARIANT_VALUE_TYPE_S32:
|
||||
instance->get_value = snmp_table_extract_value_from_s32ref;
|
||||
break;
|
||||
case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
|
||||
case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
|
||||
instance->get_value = snmp_table_extract_value_from_refconstptr;
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
|
||||
return SNMP_ERR_GENERROR;
|
||||
}
|
||||
|
||||
ret = SNMP_ERR_NOERROR;
|
||||
} else {
|
||||
ret = SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
snmp_err_t snmp_table_simple_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
|
||||
{
|
||||
const struct snmp_table_simple_node* table_node = (const struct snmp_table_simple_node*)(const void*)instance->node;
|
||||
const struct snmp_table_simple_col_def* col_def;
|
||||
struct snmp_obj_id row_oid;
|
||||
u32_t column = 0;
|
||||
snmp_err_t result;
|
||||
|
||||
LWIP_UNUSED_ARG(root_oid);
|
||||
LWIP_UNUSED_ARG(root_oid_len);
|
||||
|
||||
/* check that first part of id is 0 or 1, referencing fixed row entry */
|
||||
if ((instance->instance_oid.len > 0) && (instance->instance_oid.id[0] > 1)) {
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
if (instance->instance_oid.len > 1) {
|
||||
column = instance->instance_oid.id[1];
|
||||
}
|
||||
if (instance->instance_oid.len > 2) {
|
||||
snmp_oid_assign(&row_oid, &(instance->instance_oid.id[2]), instance->instance_oid.len - 2);
|
||||
} else {
|
||||
row_oid.len = 0;
|
||||
}
|
||||
|
||||
/* resolve column and value */
|
||||
do {
|
||||
u32_t i;
|
||||
const struct snmp_table_simple_col_def* next_col_def = NULL;
|
||||
col_def = table_node->columns;
|
||||
|
||||
for (i = 0; i < table_node->column_count; i++) {
|
||||
if (col_def->index == column) {
|
||||
next_col_def = col_def;
|
||||
break;
|
||||
} else if ((col_def->index > column) && ((next_col_def == NULL) ||
|
||||
(col_def->index < next_col_def->index))) {
|
||||
next_col_def = col_def;
|
||||
}
|
||||
col_def++;
|
||||
}
|
||||
|
||||
if (next_col_def == NULL) {
|
||||
/* no further column found */
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
result = table_node->get_next_cell_instance_and_value(
|
||||
&next_col_def->index,
|
||||
&row_oid,
|
||||
&instance->reference,
|
||||
&instance->reference_len);
|
||||
|
||||
if (result == SNMP_ERR_NOERROR) {
|
||||
col_def = next_col_def;
|
||||
break;
|
||||
}
|
||||
|
||||
row_oid.len = 0; /* reset row_oid because we switch to next column and start with the first entry there */
|
||||
column = next_col_def->index + 1;
|
||||
}
|
||||
while (1);
|
||||
|
||||
instance->asn1_type = col_def->asn1_type;
|
||||
instance->access = SNMP_NODE_INSTANCE_READ_ONLY;
|
||||
instance->set_test = NULL;
|
||||
instance->set_value = NULL;
|
||||
|
||||
switch (col_def->data_type) {
|
||||
case SNMP_VARIANT_VALUE_TYPE_U32:
|
||||
instance->get_value = snmp_table_extract_value_from_u32ref;
|
||||
break;
|
||||
case SNMP_VARIANT_VALUE_TYPE_S32:
|
||||
instance->get_value = snmp_table_extract_value_from_s32ref;
|
||||
break;
|
||||
case SNMP_VARIANT_VALUE_TYPE_PTR: /* fall through */
|
||||
case SNMP_VARIANT_VALUE_TYPE_CONST_PTR:
|
||||
instance->get_value = snmp_table_extract_value_from_refconstptr;
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(SNMP_DEBUG, ("snmp_table_simple_get_instance(): unknown column data_type: %d\n", col_def->data_type));
|
||||
return SNMP_ERR_GENERROR;
|
||||
}
|
||||
|
||||
/* build resulting oid */
|
||||
instance->instance_oid.len = 2;
|
||||
instance->instance_oid.id[0] = 1;
|
||||
instance->instance_oid.id[1] = col_def->index;
|
||||
snmp_oid_append(&instance->instance_oid, row_oid.id, row_oid.len);
|
||||
|
||||
return SNMP_ERR_NOERROR;
|
||||
}
|
||||
|
||||
|
||||
s16_t
|
||||
snmp_table_extract_value_from_s32ref(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
s32_t *dst = (s32_t*)value;
|
||||
*dst = instance->reference.s32;
|
||||
return sizeof(*dst);
|
||||
}
|
||||
|
||||
s16_t
|
||||
snmp_table_extract_value_from_u32ref(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
u32_t *dst = (u32_t*)value;
|
||||
*dst = instance->reference.u32;
|
||||
return sizeof(*dst);
|
||||
}
|
||||
|
||||
s16_t
|
||||
snmp_table_extract_value_from_refconstptr(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
MEMCPY(value, instance->reference.const_ptr, instance->reference_len);
|
||||
return (u16_t)instance->reference_len;
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
218
src/apps/snmp/snmp_threadsync.c
Normal file
218
src/apps/snmp/snmp_threadsync.c
Normal file
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMP thread synchronization implementation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP && (NO_SYS == 0) /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/apps/snmp_threadsync.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "lwip/sys.h"
|
||||
#include <string.h>
|
||||
|
||||
static void
|
||||
call_synced_function(struct threadsync_data *call_data, snmp_threadsync_called_fn fn)
|
||||
{
|
||||
sys_mutex_lock(&call_data->threadsync_node->instance->sem_usage_mutex);
|
||||
call_data->threadsync_node->instance->sync_fn(fn, call_data);
|
||||
sys_sem_wait(&call_data->threadsync_node->instance->sem);
|
||||
sys_mutex_unlock(&call_data->threadsync_node->instance->sem_usage_mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
threadsync_get_value_synced(void *ctx)
|
||||
{
|
||||
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
|
||||
|
||||
call_data->retval.s16 = call_data->proxy_instance.get_value(&call_data->proxy_instance, call_data->arg1.value);
|
||||
|
||||
sys_sem_signal(&call_data->threadsync_node->instance->sem);
|
||||
}
|
||||
|
||||
static s16_t
|
||||
threadsync_get_value(struct snmp_node_instance* instance, void* value)
|
||||
{
|
||||
struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
|
||||
|
||||
call_data->arg1.value = value;
|
||||
call_synced_function(call_data, threadsync_get_value_synced);
|
||||
|
||||
return call_data->retval.s16;
|
||||
}
|
||||
|
||||
static void
|
||||
threadsync_set_test_synced(void *ctx)
|
||||
{
|
||||
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
|
||||
|
||||
call_data->retval.err = call_data->proxy_instance.set_test(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
|
||||
|
||||
sys_sem_signal(&call_data->threadsync_node->instance->sem);
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
threadsync_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
|
||||
{
|
||||
struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
|
||||
|
||||
call_data->arg1.value = value;
|
||||
call_data->arg2.len = len;
|
||||
call_synced_function(call_data, threadsync_set_test_synced);
|
||||
|
||||
return call_data->retval.err;
|
||||
}
|
||||
|
||||
static void
|
||||
threadsync_set_value_synced(void *ctx)
|
||||
{
|
||||
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
|
||||
|
||||
call_data->retval.err = call_data->proxy_instance.set_value(&call_data->proxy_instance, call_data->arg2.len, call_data->arg1.value);
|
||||
|
||||
sys_sem_signal(&call_data->threadsync_node->instance->sem);
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
threadsync_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
|
||||
{
|
||||
struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
|
||||
|
||||
call_data->arg1.value = value;
|
||||
call_data->arg2.len = len;
|
||||
call_synced_function(call_data, threadsync_set_value_synced);
|
||||
|
||||
return call_data->retval.err;
|
||||
}
|
||||
|
||||
static void
|
||||
threadsync_release_instance_synced(void* ctx)
|
||||
{
|
||||
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
|
||||
|
||||
call_data->proxy_instance.release_instance(&call_data->proxy_instance);
|
||||
|
||||
sys_sem_signal(&call_data->threadsync_node->instance->sem);
|
||||
}
|
||||
|
||||
static void
|
||||
threadsync_release_instance(struct snmp_node_instance *instance)
|
||||
{
|
||||
struct threadsync_data *call_data = (struct threadsync_data*)instance->reference.ptr;
|
||||
|
||||
if (call_data->proxy_instance.release_instance != NULL) {
|
||||
call_synced_function(call_data, threadsync_release_instance_synced);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_instance_synced(void* ctx)
|
||||
{
|
||||
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
|
||||
const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node;
|
||||
|
||||
call_data->retval.err = leaf->get_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
|
||||
|
||||
sys_sem_signal(&call_data->threadsync_node->instance->sem);
|
||||
}
|
||||
|
||||
static void
|
||||
get_next_instance_synced(void* ctx)
|
||||
{
|
||||
struct threadsync_data *call_data = (struct threadsync_data*)ctx;
|
||||
const struct snmp_leaf_node *leaf = (const struct snmp_leaf_node*)(const void*)call_data->proxy_instance.node;
|
||||
|
||||
call_data->retval.err = leaf->get_next_instance(call_data->arg1.root_oid, call_data->arg2.root_oid_len, &call_data->proxy_instance);
|
||||
|
||||
sys_sem_signal(&call_data->threadsync_node->instance->sem);
|
||||
}
|
||||
|
||||
static snmp_err_t
|
||||
do_sync(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance, snmp_threadsync_called_fn fn)
|
||||
{
|
||||
const struct snmp_threadsync_node *threadsync_node = (const struct snmp_threadsync_node*)(const void*)instance->node;
|
||||
struct threadsync_data *call_data = &threadsync_node->instance->data;
|
||||
|
||||
if (threadsync_node->node.node.oid != threadsync_node->target->node.oid) {
|
||||
LWIP_DEBUGF(SNMP_DEBUG, ("Sync node OID does not match target node OID"));
|
||||
return SNMP_ERR_NOSUCHINSTANCE;
|
||||
}
|
||||
|
||||
memset(&call_data->proxy_instance, 0, sizeof(call_data->proxy_instance));
|
||||
|
||||
instance->reference.ptr = call_data;
|
||||
snmp_oid_assign(&call_data->proxy_instance.instance_oid, instance->instance_oid.id, instance->instance_oid.len);
|
||||
|
||||
call_data->proxy_instance.node = &threadsync_node->target->node;
|
||||
call_data->threadsync_node = threadsync_node;
|
||||
|
||||
call_data->arg1.root_oid = root_oid;
|
||||
call_data->arg2.root_oid_len = root_oid_len;
|
||||
call_synced_function(call_data, fn);
|
||||
|
||||
if (call_data->retval.err == SNMP_ERR_NOERROR) {
|
||||
instance->access = call_data->proxy_instance.access;
|
||||
instance->asn1_type = call_data->proxy_instance.asn1_type;
|
||||
instance->release_instance = threadsync_release_instance;
|
||||
instance->get_value = (call_data->proxy_instance.get_value != NULL)? threadsync_get_value : NULL;
|
||||
instance->set_value = (call_data->proxy_instance.set_value != NULL)? threadsync_set_value : NULL;
|
||||
instance->set_test = (call_data->proxy_instance.set_test != NULL)? threadsync_set_test : NULL;
|
||||
snmp_oid_assign(&instance->instance_oid, call_data->proxy_instance.instance_oid.id, call_data->proxy_instance.instance_oid.len);
|
||||
}
|
||||
|
||||
return call_data->retval.err;
|
||||
}
|
||||
|
||||
snmp_err_t
|
||||
snmp_threadsync_get_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
|
||||
{
|
||||
return do_sync(root_oid, root_oid_len, instance, get_instance_synced);
|
||||
}
|
||||
|
||||
snmp_err_t
|
||||
snmp_threadsync_get_next_instance(const u32_t *root_oid, u8_t root_oid_len, struct snmp_node_instance* instance)
|
||||
{
|
||||
return do_sync(root_oid, root_oid_len, instance, get_next_instance_synced);
|
||||
}
|
||||
|
||||
/** Initializes thread synchronization instance */
|
||||
void snmp_threadsync_init(struct snmp_threadsync_instance *instance, snmp_threadsync_synchronizer_fn sync_fn)
|
||||
{
|
||||
err_t err = sys_mutex_new(&instance->sem_usage_mutex);
|
||||
LWIP_ASSERT("Failed to set up mutex", err == ERR_OK);
|
||||
err = sys_sem_new(&instance->sem, 0);
|
||||
LWIP_ASSERT("Failed to set up semaphore", err == ERR_OK);
|
||||
instance->sync_fn = sync_fn;
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
445
src/apps/snmp/snmp_traps.c
Normal file
445
src/apps/snmp/snmp_traps.c
Normal file
@@ -0,0 +1,445 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMPv1 traps implementation.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Martin Hentschel
|
||||
* Christiaan Simons <christiaan.simons@axon.tv>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/apps/snmp.h"
|
||||
#include "lwip/apps/snmp_core.h"
|
||||
#include "snmp_msg.h"
|
||||
#include "snmp_asn1.h"
|
||||
#include "snmp_core_priv.h"
|
||||
|
||||
struct snmp_msg_trap
|
||||
{
|
||||
/* source enterprise ID (sysObjectID) */
|
||||
const struct snmp_obj_id *enterprise;
|
||||
/* source IP address, raw network order format */
|
||||
ip_addr_t sip;
|
||||
/* generic trap code */
|
||||
u32_t gen_trap;
|
||||
/* specific trap code */
|
||||
u32_t spc_trap;
|
||||
/* timestamp */
|
||||
u32_t ts;
|
||||
/* snmp_version */
|
||||
u32_t snmp_version;
|
||||
|
||||
/* output trap lengths used in ASN encoding */
|
||||
/* encoding pdu length */
|
||||
u16_t pdulen;
|
||||
/* encoding community length */
|
||||
u16_t comlen;
|
||||
/* encoding sequence length */
|
||||
u16_t seqlen;
|
||||
/* encoding varbinds sequence length */
|
||||
u16_t vbseqlen;
|
||||
};
|
||||
|
||||
static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds);
|
||||
static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len);
|
||||
static void snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
|
||||
static void snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds);
|
||||
|
||||
/** Agent community string for sending traps */
|
||||
extern const char *snmp_community_trap;
|
||||
|
||||
void* snmp_traps_handle;
|
||||
|
||||
struct snmp_trap_dst
|
||||
{
|
||||
/* destination IP address in network order */
|
||||
ip_addr_t dip;
|
||||
/* set to 0 when disabled, >0 when enabled */
|
||||
u8_t enable;
|
||||
};
|
||||
static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
|
||||
|
||||
static u8_t snmp_auth_traps_enabled = 0;
|
||||
|
||||
/**
|
||||
* @ingroup snmp_traps
|
||||
* Sets enable switch for this trap destination.
|
||||
* @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
|
||||
* @param enable switch if 0 destination is disabled >0 enabled.
|
||||
*/
|
||||
void
|
||||
snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
|
||||
{
|
||||
if (dst_idx < SNMP_TRAP_DESTINATIONS) {
|
||||
trap_dst[dst_idx].enable = enable;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_traps
|
||||
* Sets IPv4 address for this trap destination.
|
||||
* @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
|
||||
* @param dst IPv4 address in host order.
|
||||
*/
|
||||
void
|
||||
snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst)
|
||||
{
|
||||
if (dst_idx < SNMP_TRAP_DESTINATIONS) {
|
||||
ip_addr_set(&trap_dst[dst_idx].dip, dst);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_traps
|
||||
* Enable/disable authentication traps
|
||||
*/
|
||||
void
|
||||
snmp_set_auth_traps_enabled(u8_t enable)
|
||||
{
|
||||
snmp_auth_traps_enabled = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_traps
|
||||
* Get authentication traps enabled state
|
||||
*/
|
||||
u8_t
|
||||
snmp_get_auth_traps_enabled(void)
|
||||
{
|
||||
return snmp_auth_traps_enabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup snmp_traps
|
||||
* Sends a generic or enterprise specific trap message.
|
||||
*
|
||||
* @param eoid points to enterprise object identifier
|
||||
* @param generic_trap is the trap code
|
||||
* @param specific_trap used for enterprise traps when generic_trap == 6
|
||||
* @param varbinds linked list of varbinds to be sent
|
||||
* @return ERR_OK when success, ERR_MEM if we're out of memory
|
||||
*
|
||||
* @note the use of the enterprise identifier field
|
||||
* is per RFC1215.
|
||||
* Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
|
||||
* and .iso.org.dod.internet.private.enterprises.yourenterprise
|
||||
* (sysObjectID) for specific traps.
|
||||
*/
|
||||
err_t
|
||||
snmp_send_trap(const struct snmp_obj_id* eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds)
|
||||
{
|
||||
struct snmp_msg_trap trap_msg;
|
||||
struct snmp_trap_dst *td;
|
||||
struct pbuf *p;
|
||||
u16_t i, tot_len;
|
||||
err_t err = ERR_OK;
|
||||
|
||||
trap_msg.snmp_version = 0;
|
||||
|
||||
for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) {
|
||||
if ((td->enable != 0) && !ip_addr_isany(&td->dip)) {
|
||||
/* lookup current source address for this dst */
|
||||
if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg.sip)) {
|
||||
if (eoid == NULL) {
|
||||
trap_msg.enterprise = snmp_get_device_enterprise_oid();
|
||||
} else {
|
||||
trap_msg.enterprise = eoid;
|
||||
}
|
||||
|
||||
trap_msg.gen_trap = generic_trap;
|
||||
if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) {
|
||||
trap_msg.spc_trap = specific_trap;
|
||||
} else {
|
||||
trap_msg.spc_trap = 0;
|
||||
}
|
||||
|
||||
MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts);
|
||||
|
||||
/* pass 0, calculate length fields */
|
||||
tot_len = snmp_trap_varbind_sum(&trap_msg, varbinds);
|
||||
tot_len = snmp_trap_header_sum(&trap_msg, tot_len);
|
||||
|
||||
/* allocate pbuf(s) */
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM);
|
||||
if (p != NULL) {
|
||||
struct snmp_pbuf_stream pbuf_stream;
|
||||
snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len);
|
||||
|
||||
/* pass 1, encode packet ino the pbuf(s) */
|
||||
snmp_trap_header_enc(&trap_msg, &pbuf_stream);
|
||||
snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds);
|
||||
|
||||
snmp_stats.outtraps++;
|
||||
snmp_stats.outpkts++;
|
||||
|
||||
/** send to the TRAP destination */
|
||||
snmp_sendto(snmp_traps_handle, p, &td->dip, SNMP_TRAP_PORT);
|
||||
pbuf_free(p);
|
||||
} else {
|
||||
err = ERR_MEM;
|
||||
}
|
||||
} else {
|
||||
/* routing error */
|
||||
err = ERR_RTE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_traps
|
||||
* Send generic SNMP trap
|
||||
*/
|
||||
err_t
|
||||
snmp_send_trap_generic(s32_t generic_trap)
|
||||
{
|
||||
static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } };
|
||||
return snmp_send_trap(&oid, generic_trap, 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_traps
|
||||
* Send specific SNMP trap with variable bindings
|
||||
*/
|
||||
err_t
|
||||
snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds)
|
||||
{
|
||||
return snmp_send_trap(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_traps
|
||||
* Send coldstart trap
|
||||
*/
|
||||
void
|
||||
snmp_coldstart_trap(void)
|
||||
{
|
||||
snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup snmp_traps
|
||||
* Send authentication failure trap (used internally by agent)
|
||||
*/
|
||||
void
|
||||
snmp_authfail_trap(void)
|
||||
{
|
||||
if (snmp_auth_traps_enabled != 0) {
|
||||
snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static u16_t
|
||||
snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds)
|
||||
{
|
||||
struct snmp_varbind *varbind;
|
||||
u16_t tot_len;
|
||||
u8_t tot_len_len;
|
||||
|
||||
tot_len = 0;
|
||||
varbind = varbinds;
|
||||
while (varbind != NULL) {
|
||||
struct snmp_varbind_len len;
|
||||
|
||||
if (snmp_varbind_length(varbind, &len) == ERR_OK) {
|
||||
tot_len += 1 + len.vb_len_len + len.vb_value_len;
|
||||
}
|
||||
|
||||
varbind = varbind->next;
|
||||
}
|
||||
|
||||
trap->vbseqlen = tot_len;
|
||||
snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len);
|
||||
tot_len += 1 + tot_len_len;
|
||||
|
||||
return tot_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums trap header field lengths from tail to head and
|
||||
* returns trap_header_lengths for second encoding pass.
|
||||
*
|
||||
* @param trap Trap message
|
||||
* @param vb_len varbind-list length
|
||||
* @return the required length for encoding the trap header
|
||||
*/
|
||||
static u16_t
|
||||
snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len)
|
||||
{
|
||||
u16_t tot_len;
|
||||
u16_t len;
|
||||
u8_t lenlen;
|
||||
|
||||
tot_len = vb_len;
|
||||
|
||||
snmp_asn1_enc_u32t_cnt(trap->ts, &len);
|
||||
snmp_asn1_enc_length_cnt(len, &lenlen);
|
||||
tot_len += 1 + len + lenlen;
|
||||
|
||||
snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len);
|
||||
snmp_asn1_enc_length_cnt(len, &lenlen);
|
||||
tot_len += 1 + len + lenlen;
|
||||
|
||||
snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len);
|
||||
snmp_asn1_enc_length_cnt(len, &lenlen);
|
||||
tot_len += 1 + len + lenlen;
|
||||
|
||||
if (IP_IS_V6_VAL(trap->sip)) {
|
||||
#if LWIP_IPV6
|
||||
len = sizeof(ip_2_ip6(&trap->sip)->addr);
|
||||
#endif
|
||||
} else {
|
||||
#if LWIP_IPV4
|
||||
len = sizeof(ip_2_ip4(&trap->sip)->addr);
|
||||
#endif
|
||||
}
|
||||
snmp_asn1_enc_length_cnt(len, &lenlen);
|
||||
tot_len += 1 + len + lenlen;
|
||||
|
||||
snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len);
|
||||
snmp_asn1_enc_length_cnt(len, &lenlen);
|
||||
tot_len += 1 + len + lenlen;
|
||||
|
||||
trap->pdulen = tot_len;
|
||||
snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen);
|
||||
tot_len += 1 + lenlen;
|
||||
|
||||
trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF);
|
||||
snmp_asn1_enc_length_cnt(trap->comlen, &lenlen);
|
||||
tot_len += 1 + lenlen + trap->comlen;
|
||||
|
||||
snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len);
|
||||
snmp_asn1_enc_length_cnt(len, &lenlen);
|
||||
tot_len += 1 + len + lenlen;
|
||||
|
||||
trap->seqlen = tot_len;
|
||||
snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen);
|
||||
tot_len += 1 + lenlen;
|
||||
|
||||
return tot_len;
|
||||
}
|
||||
|
||||
static void
|
||||
snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds)
|
||||
{
|
||||
struct snmp_asn1_tlv tlv;
|
||||
struct snmp_varbind *varbind;
|
||||
|
||||
varbind = varbinds;
|
||||
|
||||
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen);
|
||||
snmp_ans1_enc_tlv(pbuf_stream, &tlv);
|
||||
|
||||
while (varbind != NULL) {
|
||||
snmp_append_outbound_varbind(pbuf_stream, varbind);
|
||||
|
||||
varbind = varbind->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes trap header from head to tail.
|
||||
*/
|
||||
static void
|
||||
snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream)
|
||||
{
|
||||
struct snmp_asn1_tlv tlv;
|
||||
|
||||
/* 'Message' sequence */
|
||||
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen);
|
||||
snmp_ans1_enc_tlv(pbuf_stream, &tlv);
|
||||
|
||||
/* version */
|
||||
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
|
||||
snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len);
|
||||
snmp_ans1_enc_tlv(pbuf_stream, &tlv);
|
||||
snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version);
|
||||
|
||||
/* community */
|
||||
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen);
|
||||
snmp_ans1_enc_tlv(pbuf_stream, &tlv);
|
||||
snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)snmp_community_trap, trap->comlen);
|
||||
|
||||
/* 'PDU' sequence */
|
||||
SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen);
|
||||
snmp_ans1_enc_tlv(pbuf_stream, &tlv);
|
||||
|
||||
/* object ID */
|
||||
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0);
|
||||
snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len);
|
||||
snmp_ans1_enc_tlv(pbuf_stream, &tlv);
|
||||
snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len);
|
||||
|
||||
/* IP addr */
|
||||
if (IP_IS_V6_VAL(trap->sip)) {
|
||||
#if LWIP_IPV6
|
||||
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr));
|
||||
snmp_ans1_enc_tlv(pbuf_stream, &tlv);
|
||||
snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr));
|
||||
#endif
|
||||
} else {
|
||||
#if LWIP_IPV4
|
||||
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr));
|
||||
snmp_ans1_enc_tlv(pbuf_stream, &tlv);
|
||||
snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* trap length */
|
||||
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
|
||||
snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len);
|
||||
snmp_ans1_enc_tlv(pbuf_stream, &tlv);
|
||||
snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap);
|
||||
|
||||
/* specific trap */
|
||||
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0);
|
||||
snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len);
|
||||
snmp_ans1_enc_tlv(pbuf_stream, &tlv);
|
||||
snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap);
|
||||
|
||||
/* timestamp */
|
||||
SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0);
|
||||
snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len);
|
||||
snmp_ans1_enc_tlv(pbuf_stream, &tlv);
|
||||
snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts);
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP */
|
||||
136
src/apps/snmp/snmpv3.c
Normal file
136
src/apps/snmp/snmpv3.c
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* @file
|
||||
* Additional SNMPv3 functionality RFC3414 and RFC3826.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Elias Oenal.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Elias Oenal <lwip@eliasoenal.com>
|
||||
*/
|
||||
|
||||
#include "snmpv3_priv.h"
|
||||
#include "lwip/apps/snmpv3.h"
|
||||
#include "lwip/sys.h"
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && LWIP_SNMP_V3
|
||||
|
||||
#ifdef LWIP_SNMPV3_INCLUDE_ENGINE
|
||||
#include LWIP_SNMPV3_INCLUDE_ENGINE
|
||||
#endif
|
||||
|
||||
#define SNMP_MAX_TIME_BOOT 2147483647UL
|
||||
|
||||
/** Call this if engine has been changed. Has to reset boots, see below */
|
||||
void
|
||||
snmpv3_engine_id_changed(void)
|
||||
{
|
||||
snmpv3_set_engine_boots(0);
|
||||
}
|
||||
|
||||
/** According to RFC3414 2.2.2.
|
||||
*
|
||||
* The number of times that the SNMP engine has
|
||||
* (re-)initialized itself since snmpEngineID
|
||||
* was last configured.
|
||||
*/
|
||||
u32_t
|
||||
snmpv3_get_engine_boots_internal(void)
|
||||
{
|
||||
if (snmpv3_get_engine_boots() == 0 ||
|
||||
snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT) {
|
||||
return snmpv3_get_engine_boots();
|
||||
}
|
||||
|
||||
snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
|
||||
return snmpv3_get_engine_boots();
|
||||
}
|
||||
|
||||
/** RFC3414 2.2.2.
|
||||
*
|
||||
* Once the timer reaches 2147483647 it gets reset to zero and the
|
||||
* engine boot ups get incremented.
|
||||
*/
|
||||
u32_t
|
||||
snmpv3_get_engine_time_internal(void)
|
||||
{
|
||||
if (snmpv3_get_engine_time() >= SNMP_MAX_TIME_BOOT) {
|
||||
snmpv3_reset_engine_time();
|
||||
|
||||
if (snmpv3_get_engine_boots() < SNMP_MAX_TIME_BOOT - 1) {
|
||||
snmpv3_set_engine_boots(snmpv3_get_engine_boots() + 1);
|
||||
} else {
|
||||
snmpv3_set_engine_boots(SNMP_MAX_TIME_BOOT);
|
||||
}
|
||||
}
|
||||
|
||||
return snmpv3_get_engine_time();
|
||||
}
|
||||
|
||||
#if LWIP_SNMP_V3_CRYPTO
|
||||
|
||||
/* This function ignores the byte order suggestion in RFC3414
|
||||
* since it simply doesn't influence the effectiveness of an IV.
|
||||
*
|
||||
* Implementing RFC3826 priv param algorithm if LWIP_RAND is available.
|
||||
*
|
||||
* @todo: This is a potential thread safety issue.
|
||||
*/
|
||||
err_t
|
||||
snmpv3_build_priv_param(u8_t* priv_param)
|
||||
{
|
||||
#ifdef LWIP_RAND /* Based on RFC3826 */
|
||||
static u8_t init;
|
||||
static u32_t priv1, priv2;
|
||||
|
||||
/* Lazy initialisation */
|
||||
if (init == 0) {
|
||||
init = 1;
|
||||
priv1 = LWIP_RAND();
|
||||
priv2 = LWIP_RAND();
|
||||
}
|
||||
|
||||
SMEMCPY(&priv_param[0], &priv1, sizeof(priv1));
|
||||
SMEMCPY(&priv_param[4], &priv2, sizeof(priv2));
|
||||
|
||||
/* Emulate 64bit increment */
|
||||
priv1++;
|
||||
if (!priv1) { /* Overflow */
|
||||
priv2++;
|
||||
}
|
||||
#else /* Based on RFC3414 */
|
||||
static u32_t ctr;
|
||||
u32_t boots = LWIP_SNMPV3_GET_ENGINE_BOOTS();
|
||||
SMEMCPY(&priv_param[0], &boots, 4);
|
||||
SMEMCPY(&priv_param[4], &ctr, 4);
|
||||
ctr++;
|
||||
#endif
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* LWIP_SNMP_V3_CRYPTO */
|
||||
|
||||
#endif
|
||||
145
src/apps/snmp/snmpv3_dummy.c
Normal file
145
src/apps/snmp/snmpv3_dummy.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* @file
|
||||
* Dummy SNMPv3 functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Elias Oenal.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Elias Oenal <lwip@eliasoenal.com>
|
||||
* Dirk Ziegelmeier <dirk@ziegelmeier.net>
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmpv3.h"
|
||||
#include "snmpv3_priv.h"
|
||||
#include <string.h>
|
||||
#include "lwip/err.h"
|
||||
|
||||
#if LWIP_SNMP && LWIP_SNMP_V3
|
||||
|
||||
/**
|
||||
* @param username is a pointer to a string.
|
||||
* @param auth_algo is a pointer to u8_t. The implementation has to set this if user was found.
|
||||
* @param auth_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
|
||||
* @param priv_algo is a pointer to u8_t. The implementation has to set this if user was found.
|
||||
* @param priv_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
|
||||
*/
|
||||
err_t
|
||||
snmpv3_get_user(const char* username, u8_t *auth_algo, u8_t *auth_key, u8_t *priv_algo, u8_t *priv_key)
|
||||
{
|
||||
const char* engine_id;
|
||||
u8_t engine_id_len;
|
||||
|
||||
if(strlen(username) == 0) {
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
if(memcmp(username, "lwip", 4) != 0) {
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
snmpv3_get_engine_id(&engine_id, &engine_id_len);
|
||||
|
||||
if(auth_key != NULL) {
|
||||
snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10,
|
||||
(const u8_t*)engine_id, engine_id_len,
|
||||
auth_key);
|
||||
*auth_algo = SNMP_V3_AUTH_ALGO_SHA;
|
||||
}
|
||||
if(priv_key != NULL) {
|
||||
snmpv3_password_to_key_sha((const u8_t*)"maplesyrup", 10,
|
||||
(const u8_t*)engine_id, engine_id_len,
|
||||
priv_key);
|
||||
*priv_algo = SNMP_V3_PRIV_ALGO_DES;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get engine ID from persistence
|
||||
* @param id
|
||||
* @param len
|
||||
*/
|
||||
void
|
||||
snmpv3_get_engine_id(const char **id, u8_t *len)
|
||||
{
|
||||
*id = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02";
|
||||
*len = 12;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store engine ID in persistence
|
||||
* @param id
|
||||
* @param len
|
||||
*/
|
||||
err_t
|
||||
snmpv3_set_engine_id(const char *id, u8_t len)
|
||||
{
|
||||
LWIP_UNUSED_ARG(id);
|
||||
LWIP_UNUSED_ARG(len);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get engine boots from persistence. Must be increased on each boot.
|
||||
* @return
|
||||
*/
|
||||
u32_t
|
||||
snmpv3_get_engine_boots(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store engine boots in persistence
|
||||
* @param boots
|
||||
*/
|
||||
void
|
||||
snmpv3_set_engine_boots(u32_t boots)
|
||||
{
|
||||
LWIP_UNUSED_ARG(boots);
|
||||
}
|
||||
|
||||
/**
|
||||
* RFC3414 2.2.2.
|
||||
* Once the timer reaches 2147483647 it gets reset to zero and the
|
||||
* engine boot ups get incremented.
|
||||
*/
|
||||
u32_t
|
||||
snmpv3_get_engine_time(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset current engine time to 0
|
||||
*/
|
||||
void
|
||||
snmpv3_reset_engine_time(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP && LWIP_SNMP_V3 */
|
||||
331
src/apps/snmp/snmpv3_mbedtls.c
Normal file
331
src/apps/snmp/snmpv3_mbedtls.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/**
|
||||
* @file
|
||||
* SNMPv3 crypto/auth functions implemented for ARM mbedtls.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Elias Oenal and Dirk Ziegelmeier.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Elias Oenal <lwip@eliasoenal.com>
|
||||
* Dirk Ziegelmeier <dirk@ziegelmeier.net>
|
||||
*/
|
||||
|
||||
#include "lwip/apps/snmpv3.h"
|
||||
#include "snmpv3_priv.h"
|
||||
#include "lwip/arch.h"
|
||||
#include "snmp_msg.h"
|
||||
#include "lwip/sys.h"
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS
|
||||
|
||||
#include "mbedtls/md.h"
|
||||
#include "mbedtls/cipher.h"
|
||||
|
||||
#include "mbedtls/md5.h"
|
||||
#include "mbedtls/sha1.h"
|
||||
|
||||
err_t
|
||||
snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length,
|
||||
const u8_t* key, u8_t algo, u8_t* hmac_out)
|
||||
{
|
||||
u32_t i;
|
||||
u8_t key_len;
|
||||
const mbedtls_md_info_t *md_info;
|
||||
mbedtls_md_context_t ctx;
|
||||
struct snmp_pbuf_stream read_stream;
|
||||
snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
|
||||
|
||||
if (algo == SNMP_V3_AUTH_ALGO_MD5) {
|
||||
md_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
|
||||
key_len = SNMP_V3_MD5_LEN;
|
||||
} else if (algo == SNMP_V3_AUTH_ALGO_SHA) {
|
||||
md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
|
||||
key_len = SNMP_V3_SHA_LEN;
|
||||
} else {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
mbedtls_md_init(&ctx);
|
||||
if(mbedtls_md_setup(&ctx, md_info, 1) != 0) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
if (mbedtls_md_hmac_starts(&ctx, key, key_len) != 0) {
|
||||
goto free_md;
|
||||
}
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
u8_t byte;
|
||||
|
||||
if (snmp_pbuf_stream_read(&read_stream, &byte)) {
|
||||
goto free_md;
|
||||
}
|
||||
|
||||
if (mbedtls_md_hmac_update(&ctx, &byte, 1) != 0) {
|
||||
goto free_md;
|
||||
}
|
||||
}
|
||||
|
||||
if (mbedtls_md_hmac_finish(&ctx, hmac_out) != 0) {
|
||||
goto free_md;
|
||||
}
|
||||
|
||||
mbedtls_md_free(&ctx);
|
||||
return ERR_OK;
|
||||
|
||||
free_md:
|
||||
mbedtls_md_free(&ctx);
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
#if LWIP_SNMP_V3_CRYPTO
|
||||
|
||||
err_t
|
||||
snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length,
|
||||
const u8_t* key, const u8_t* priv_param, const u32_t engine_boots,
|
||||
const u32_t engine_time, u8_t algo, u8_t mode)
|
||||
{
|
||||
size_t i;
|
||||
mbedtls_cipher_context_t ctx;
|
||||
const mbedtls_cipher_info_t *cipher_info;
|
||||
|
||||
struct snmp_pbuf_stream read_stream;
|
||||
struct snmp_pbuf_stream write_stream;
|
||||
snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset, stream->length);
|
||||
snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset, stream->length);
|
||||
mbedtls_cipher_init(&ctx);
|
||||
|
||||
if (algo == SNMP_V3_PRIV_ALGO_DES) {
|
||||
u8_t iv_local[8];
|
||||
u8_t out_bytes[8];
|
||||
size_t out_len;
|
||||
|
||||
/* RFC 3414 mandates padding for DES */
|
||||
if ((length & 0x07) != 0) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_CBC);
|
||||
if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if(mbedtls_cipher_set_padding_mode(&ctx, MBEDTLS_PADDING_NONE) != 0) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if(mbedtls_cipher_setkey(&ctx, key, 8*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Prepare IV */
|
||||
for (i = 0; i < LWIP_ARRAYSIZE(iv_local); i++) {
|
||||
iv_local[i] = priv_param[i] ^ key[i + 8];
|
||||
}
|
||||
if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < length; i += 8) {
|
||||
size_t j;
|
||||
u8_t in_bytes[8];
|
||||
out_len = LWIP_ARRAYSIZE(out_bytes) ;
|
||||
|
||||
for (j = 0; j < LWIP_ARRAYSIZE(in_bytes); j++) {
|
||||
snmp_pbuf_stream_read(&read_stream, &in_bytes[j]);
|
||||
}
|
||||
|
||||
if(mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len);
|
||||
}
|
||||
|
||||
out_len = LWIP_ARRAYSIZE(out_bytes);
|
||||
if(mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) {
|
||||
goto error;
|
||||
}
|
||||
snmp_pbuf_stream_writebuf(&write_stream, out_bytes, out_len);
|
||||
} else if (algo == SNMP_V3_PRIV_ALGO_AES) {
|
||||
u8_t iv_local[16];
|
||||
|
||||
cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_CFB128);
|
||||
if(mbedtls_cipher_setup(&ctx, cipher_info) != 0) {
|
||||
return ERR_ARG;
|
||||
}
|
||||
if(mbedtls_cipher_setkey(&ctx, key, 16*8, (mode == SNMP_V3_PRIV_MODE_ENCRYPT)? MBEDTLS_ENCRYPT : MBEDTLS_DECRYPT) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* IV is the big endian concatenation of boots,
|
||||
* uptime and priv param - see RFC3826.
|
||||
*/
|
||||
iv_local[0 + 0] = (engine_boots >> 24) & 0xFF;
|
||||
iv_local[0 + 1] = (engine_boots >> 16) & 0xFF;
|
||||
iv_local[0 + 2] = (engine_boots >> 8) & 0xFF;
|
||||
iv_local[0 + 3] = (engine_boots >> 0) & 0xFF;
|
||||
iv_local[4 + 0] = (engine_time >> 24) & 0xFF;
|
||||
iv_local[4 + 1] = (engine_time >> 16) & 0xFF;
|
||||
iv_local[4 + 2] = (engine_time >> 8) & 0xFF;
|
||||
iv_local[4 + 3] = (engine_time >> 0) & 0xFF;
|
||||
SMEMCPY(iv_local + 8, priv_param, 8);
|
||||
if(mbedtls_cipher_set_iv(&ctx, iv_local, LWIP_ARRAYSIZE(iv_local)) != 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
u8_t in_byte;
|
||||
u8_t out_byte;
|
||||
size_t out_len = sizeof(out_byte);
|
||||
|
||||
snmp_pbuf_stream_read(&read_stream, &in_byte);
|
||||
if(mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) {
|
||||
goto error;
|
||||
}
|
||||
snmp_pbuf_stream_write(&write_stream, out_byte);
|
||||
}
|
||||
} else {
|
||||
return ERR_ARG;
|
||||
}
|
||||
|
||||
mbedtls_cipher_free(&ctx);
|
||||
return ERR_OK;
|
||||
|
||||
error:
|
||||
mbedtls_cipher_free(&ctx);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP_V3_CRYPTO */
|
||||
|
||||
/* A.2.1. Password to Key Sample Code for MD5 */
|
||||
void
|
||||
snmpv3_password_to_key_md5(
|
||||
const u8_t *password, /* IN */
|
||||
u8_t passwordlen, /* IN */
|
||||
const u8_t *engineID, /* IN - pointer to snmpEngineID */
|
||||
u8_t engineLength,/* IN - length of snmpEngineID */
|
||||
u8_t *key) /* OUT - pointer to caller 16-octet buffer */
|
||||
{
|
||||
mbedtls_md5_context MD;
|
||||
u8_t *cp, password_buf[64];
|
||||
u32_t password_index = 0;
|
||||
u8_t i;
|
||||
u32_t count = 0;
|
||||
|
||||
mbedtls_md5_init(&MD); /* initialize MD5 */
|
||||
mbedtls_md5_starts(&MD);
|
||||
|
||||
/**********************************************/
|
||||
/* Use while loop until we've done 1 Megabyte */
|
||||
/**********************************************/
|
||||
while (count < 1048576) {
|
||||
cp = password_buf;
|
||||
for (i = 0; i < 64; i++) {
|
||||
/*************************************************/
|
||||
/* Take the next octet of the password, wrapping */
|
||||
/* to the beginning of the password as necessary.*/
|
||||
/*************************************************/
|
||||
*cp++ = password[password_index++ % passwordlen];
|
||||
}
|
||||
mbedtls_md5_update(&MD, password_buf, 64);
|
||||
count += 64;
|
||||
}
|
||||
mbedtls_md5_finish(&MD, key); /* tell MD5 we're done */
|
||||
|
||||
/*****************************************************/
|
||||
/* Now localize the key with the engineID and pass */
|
||||
/* through MD5 to produce final key */
|
||||
/* May want to ensure that engineLength <= 32, */
|
||||
/* otherwise need to use a buffer larger than 64 */
|
||||
/*****************************************************/
|
||||
SMEMCPY(password_buf, key, 16);
|
||||
MEMCPY(password_buf + 16, engineID, engineLength);
|
||||
SMEMCPY(password_buf + 16 + engineLength, key, 16);
|
||||
|
||||
mbedtls_md5_starts(&MD);
|
||||
mbedtls_md5_update(&MD, password_buf, 32 + engineLength);
|
||||
mbedtls_md5_finish(&MD, key);
|
||||
|
||||
mbedtls_md5_free(&MD);
|
||||
return;
|
||||
}
|
||||
|
||||
/* A.2.2. Password to Key Sample Code for SHA */
|
||||
void
|
||||
snmpv3_password_to_key_sha(
|
||||
const u8_t *password, /* IN */
|
||||
u8_t passwordlen, /* IN */
|
||||
const u8_t *engineID, /* IN - pointer to snmpEngineID */
|
||||
u8_t engineLength,/* IN - length of snmpEngineID */
|
||||
u8_t *key) /* OUT - pointer to caller 20-octet buffer */
|
||||
{
|
||||
mbedtls_sha1_context SH;
|
||||
u8_t *cp, password_buf[72];
|
||||
u32_t password_index = 0;
|
||||
u8_t i;
|
||||
u32_t count = 0;
|
||||
|
||||
mbedtls_sha1_init(&SH); /* initialize SHA */
|
||||
mbedtls_sha1_starts(&SH);
|
||||
|
||||
/**********************************************/
|
||||
/* Use while loop until we've done 1 Megabyte */
|
||||
/**********************************************/
|
||||
while (count < 1048576) {
|
||||
cp = password_buf;
|
||||
for (i = 0; i < 64; i++) {
|
||||
/*************************************************/
|
||||
/* Take the next octet of the password, wrapping */
|
||||
/* to the beginning of the password as necessary.*/
|
||||
/*************************************************/
|
||||
*cp++ = password[password_index++ % passwordlen];
|
||||
}
|
||||
mbedtls_sha1_update(&SH, password_buf, 64);
|
||||
count += 64;
|
||||
}
|
||||
mbedtls_sha1_finish(&SH, key); /* tell SHA we're done */
|
||||
|
||||
/*****************************************************/
|
||||
/* Now localize the key with the engineID and pass */
|
||||
/* through SHA to produce final key */
|
||||
/* May want to ensure that engineLength <= 32, */
|
||||
/* otherwise need to use a buffer larger than 72 */
|
||||
/*****************************************************/
|
||||
SMEMCPY(password_buf, key, 20);
|
||||
MEMCPY(password_buf + 20, engineID, engineLength);
|
||||
SMEMCPY(password_buf + 20 + engineLength, key, 20);
|
||||
|
||||
mbedtls_sha1_starts(&SH);
|
||||
mbedtls_sha1_update(&SH, password_buf, 40 + engineLength);
|
||||
mbedtls_sha1_finish(&SH, key);
|
||||
|
||||
mbedtls_sha1_free(&SH);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* LWIP_SNMP && LWIP_SNMP_V3 && LWIP_SNMP_V3_MBEDTLS */
|
||||
66
src/apps/snmp/snmpv3_priv.h
Normal file
66
src/apps/snmp/snmpv3_priv.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* @file
|
||||
* Additional SNMPv3 functionality RFC3414 and RFC3826 (internal API, do not use in client code).
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Elias Oenal.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Elias Oenal <lwip@eliasoenal.com>
|
||||
*/
|
||||
|
||||
#ifndef LWIP_HDR_APPS_SNMP_V3_PRIV_H
|
||||
#define LWIP_HDR_APPS_SNMP_V3_PRIV_H
|
||||
|
||||
#include "lwip/apps/snmp_opts.h"
|
||||
|
||||
#if LWIP_SNMP && LWIP_SNMP_V3
|
||||
|
||||
#include "snmp_pbuf_stream.h"
|
||||
|
||||
/* According to RFC 3411 */
|
||||
#define SNMP_V3_MAX_ENGINE_ID_LENGTH 32
|
||||
#define SNMP_V3_MAX_USER_LENGTH 32
|
||||
|
||||
#define SNMP_V3_MAX_AUTH_PARAM_LENGTH 12
|
||||
#define SNMP_V3_MAX_PRIV_PARAM_LENGTH 8
|
||||
|
||||
#define SNMP_V3_AUTH_FLAG 0x01
|
||||
#define SNMP_V3_PRIV_FLAG 0x02
|
||||
|
||||
#define SNMP_V3_MD5_LEN 16
|
||||
#define SNMP_V3_SHA_LEN 20
|
||||
|
||||
u32_t snmpv3_get_engine_boots_internal(void);
|
||||
u32_t snmpv3_get_engine_time_internal(void);
|
||||
err_t snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key, u8_t algo, u8_t* hmac_out);
|
||||
err_t snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key,
|
||||
const u8_t* priv_param, const u32_t engine_boots, const u32_t engine_time, u8_t algo, u8_t mode);
|
||||
err_t snmpv3_build_priv_param(u8_t* priv_param);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* LWIP_HDR_APPS_SNMP_V3_PRIV_H */
|
||||
726
src/apps/sntp/sntp.c
Normal file
726
src/apps/sntp/sntp.c
Normal file
@@ -0,0 +1,726 @@
|
||||
/**
|
||||
* @file
|
||||
* SNTP client module
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007-2009 Frédéric Bernon, Simon Goldschmidt
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Frédéric Bernon, Simon Goldschmidt
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup sntp SNTP
|
||||
* @ingroup apps
|
||||
*
|
||||
* This is simple "SNTP" client for the lwIP raw API.
|
||||
* It is a minimal implementation of SNTPv4 as specified in RFC 4330.
|
||||
*
|
||||
* For a list of some public NTP servers, see this link :
|
||||
* http://support.ntp.org/bin/view/Servers/NTPPoolServers
|
||||
*
|
||||
* @todo:
|
||||
* - set/change servers at runtime
|
||||
* - complete SNTP_CHECK_RESPONSE checks 3 and 4
|
||||
*/
|
||||
|
||||
#include "lwip/apps/sntp.h"
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/timeouts.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/dhcp.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#if LWIP_UDP
|
||||
|
||||
/* Handle support for more than one server via SNTP_MAX_SERVERS */
|
||||
#if SNTP_MAX_SERVERS > 1
|
||||
#define SNTP_SUPPORT_MULTIPLE_SERVERS 1
|
||||
#else /* NTP_MAX_SERVERS > 1 */
|
||||
#define SNTP_SUPPORT_MULTIPLE_SERVERS 0
|
||||
#endif /* NTP_MAX_SERVERS > 1 */
|
||||
|
||||
#if (SNTP_UPDATE_DELAY < 15000) && !defined(SNTP_SUPPRESS_DELAY_CHECK)
|
||||
#error "SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds (define SNTP_SUPPRESS_DELAY_CHECK to disable this error)!"
|
||||
#endif
|
||||
|
||||
/* Configure behaviour depending on microsecond or second precision */
|
||||
#ifdef SNTP_SET_SYSTEM_TIME_US
|
||||
#define SNTP_CALC_TIME_US 1
|
||||
#define SNTP_RECEIVE_TIME_SIZE 2
|
||||
#else
|
||||
#define SNTP_SET_SYSTEM_TIME_US(sec, us)
|
||||
#define SNTP_CALC_TIME_US 0
|
||||
#define SNTP_RECEIVE_TIME_SIZE 1
|
||||
#endif
|
||||
|
||||
|
||||
/* the various debug levels for this file */
|
||||
#define SNTP_DEBUG_TRACE (SNTP_DEBUG | LWIP_DBG_TRACE)
|
||||
#define SNTP_DEBUG_STATE (SNTP_DEBUG | LWIP_DBG_STATE)
|
||||
#define SNTP_DEBUG_WARN (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
|
||||
#define SNTP_DEBUG_WARN_STATE (SNTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
|
||||
#define SNTP_DEBUG_SERIOUS (SNTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
|
||||
|
||||
#define SNTP_ERR_KOD 1
|
||||
|
||||
/* SNTP protocol defines */
|
||||
#define SNTP_MSG_LEN 48
|
||||
|
||||
#define SNTP_OFFSET_LI_VN_MODE 0
|
||||
#define SNTP_LI_MASK 0xC0
|
||||
#define SNTP_LI_NO_WARNING 0x00
|
||||
#define SNTP_LI_LAST_MINUTE_61_SEC 0x01
|
||||
#define SNTP_LI_LAST_MINUTE_59_SEC 0x02
|
||||
#define SNTP_LI_ALARM_CONDITION 0x03 /* (clock not synchronized) */
|
||||
|
||||
#define SNTP_VERSION_MASK 0x38
|
||||
#define SNTP_VERSION (4/* NTP Version 4*/<<3)
|
||||
|
||||
#define SNTP_MODE_MASK 0x07
|
||||
#define SNTP_MODE_CLIENT 0x03
|
||||
#define SNTP_MODE_SERVER 0x04
|
||||
#define SNTP_MODE_BROADCAST 0x05
|
||||
|
||||
#define SNTP_OFFSET_STRATUM 1
|
||||
#define SNTP_STRATUM_KOD 0x00
|
||||
|
||||
#define SNTP_OFFSET_ORIGINATE_TIME 24
|
||||
#define SNTP_OFFSET_RECEIVE_TIME 32
|
||||
#define SNTP_OFFSET_TRANSMIT_TIME 40
|
||||
|
||||
/* number of seconds between 1900 and 1970 (MSB=1)*/
|
||||
#define DIFF_SEC_1900_1970 (2208988800UL)
|
||||
/* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */
|
||||
#define DIFF_SEC_1970_2036 (2085978496UL)
|
||||
|
||||
/**
|
||||
* SNTP packet format (without optional fields)
|
||||
* Timestamps are coded as 64 bits:
|
||||
* - 32 bits seconds since Jan 01, 1970, 00:00
|
||||
* - 32 bits seconds fraction (0-padded)
|
||||
* For future use, if the MSB in the seconds part is set, seconds are based
|
||||
* on Feb 07, 2036, 06:28:16.
|
||||
*/
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct sntp_msg {
|
||||
PACK_STRUCT_FLD_8(u8_t li_vn_mode);
|
||||
PACK_STRUCT_FLD_8(u8_t stratum);
|
||||
PACK_STRUCT_FLD_8(u8_t poll);
|
||||
PACK_STRUCT_FLD_8(u8_t precision);
|
||||
PACK_STRUCT_FIELD(u32_t root_delay);
|
||||
PACK_STRUCT_FIELD(u32_t root_dispersion);
|
||||
PACK_STRUCT_FIELD(u32_t reference_identifier);
|
||||
PACK_STRUCT_FIELD(u32_t reference_timestamp[2]);
|
||||
PACK_STRUCT_FIELD(u32_t originate_timestamp[2]);
|
||||
PACK_STRUCT_FIELD(u32_t receive_timestamp[2]);
|
||||
PACK_STRUCT_FIELD(u32_t transmit_timestamp[2]);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
/* function prototypes */
|
||||
static void sntp_request(void *arg);
|
||||
|
||||
/** The operating mode */
|
||||
static u8_t sntp_opmode;
|
||||
|
||||
/** The UDP pcb used by the SNTP client */
|
||||
static struct udp_pcb* sntp_pcb;
|
||||
/** Names/Addresses of servers */
|
||||
struct sntp_server {
|
||||
#if SNTP_SERVER_DNS
|
||||
char* name;
|
||||
#endif /* SNTP_SERVER_DNS */
|
||||
ip_addr_t addr;
|
||||
};
|
||||
static struct sntp_server sntp_servers[SNTP_MAX_SERVERS];
|
||||
|
||||
#if SNTP_GET_SERVERS_FROM_DHCP
|
||||
static u8_t sntp_set_servers_from_dhcp;
|
||||
#endif /* SNTP_GET_SERVERS_FROM_DHCP */
|
||||
#if SNTP_SUPPORT_MULTIPLE_SERVERS
|
||||
/** The currently used server (initialized to 0) */
|
||||
static u8_t sntp_current_server;
|
||||
#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
|
||||
#define sntp_current_server 0
|
||||
#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
|
||||
|
||||
#if SNTP_RETRY_TIMEOUT_EXP
|
||||
#define SNTP_RESET_RETRY_TIMEOUT() sntp_retry_timeout = SNTP_RETRY_TIMEOUT
|
||||
/** Retry time, initialized with SNTP_RETRY_TIMEOUT and doubled with each retry. */
|
||||
static u32_t sntp_retry_timeout;
|
||||
#else /* SNTP_RETRY_TIMEOUT_EXP */
|
||||
#define SNTP_RESET_RETRY_TIMEOUT()
|
||||
#define sntp_retry_timeout SNTP_RETRY_TIMEOUT
|
||||
#endif /* SNTP_RETRY_TIMEOUT_EXP */
|
||||
|
||||
#if SNTP_CHECK_RESPONSE >= 1
|
||||
/** Saves the last server address to compare with response */
|
||||
static ip_addr_t sntp_last_server_address;
|
||||
#endif /* SNTP_CHECK_RESPONSE >= 1 */
|
||||
|
||||
#if SNTP_CHECK_RESPONSE >= 2
|
||||
/** Saves the last timestamp sent (which is sent back by the server)
|
||||
* to compare against in response */
|
||||
static u32_t sntp_last_timestamp_sent[2];
|
||||
#endif /* SNTP_CHECK_RESPONSE >= 2 */
|
||||
|
||||
/**
|
||||
* SNTP processing of received timestamp
|
||||
*/
|
||||
static void
|
||||
sntp_process(u32_t *receive_timestamp)
|
||||
{
|
||||
/* convert SNTP time (1900-based) to unix GMT time (1970-based)
|
||||
* if MSB is 0, SNTP time is 2036-based!
|
||||
*/
|
||||
u32_t rx_secs = lwip_ntohl(receive_timestamp[0]);
|
||||
int is_1900_based = ((rx_secs & 0x80000000) != 0);
|
||||
u32_t t = is_1900_based ? (rx_secs - DIFF_SEC_1900_1970) : (rx_secs + DIFF_SEC_1970_2036);
|
||||
time_t tim = t;
|
||||
|
||||
#if SNTP_CALC_TIME_US
|
||||
u32_t us = lwip_ntohl(receive_timestamp[1]) / 4295;
|
||||
SNTP_SET_SYSTEM_TIME_US(t, us);
|
||||
/* display local time from GMT time */
|
||||
LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %"U32_F" us", ctime(&tim), us));
|
||||
|
||||
#else /* SNTP_CALC_TIME_US */
|
||||
|
||||
/* change system time and/or the update the RTC clock */
|
||||
SNTP_SET_SYSTEM_TIME(t);
|
||||
/* display local time from GMT time */
|
||||
LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s", ctime(&tim)));
|
||||
#endif /* SNTP_CALC_TIME_US */
|
||||
LWIP_UNUSED_ARG(tim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize request struct to be sent to server.
|
||||
*/
|
||||
static void
|
||||
sntp_initialize_request(struct sntp_msg *req)
|
||||
{
|
||||
memset(req, 0, SNTP_MSG_LEN);
|
||||
req->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION | SNTP_MODE_CLIENT;
|
||||
|
||||
#if SNTP_CHECK_RESPONSE >= 2
|
||||
{
|
||||
u32_t sntp_time_sec, sntp_time_us;
|
||||
/* fill in transmit timestamp and save it in 'sntp_last_timestamp_sent' */
|
||||
SNTP_GET_SYSTEM_TIME(sntp_time_sec, sntp_time_us);
|
||||
sntp_last_timestamp_sent[0] = lwip_htonl(sntp_time_sec + DIFF_SEC_1900_1970);
|
||||
req->transmit_timestamp[0] = sntp_last_timestamp_sent[0];
|
||||
/* we send/save us instead of fraction to be faster... */
|
||||
sntp_last_timestamp_sent[1] = lwip_htonl(sntp_time_us);
|
||||
req->transmit_timestamp[1] = sntp_last_timestamp_sent[1];
|
||||
}
|
||||
#endif /* SNTP_CHECK_RESPONSE >= 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry: send a new request (and increase retry timeout).
|
||||
*
|
||||
* @param arg is unused (only necessary to conform to sys_timeout)
|
||||
*/
|
||||
static void
|
||||
sntp_retry(void* arg)
|
||||
{
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
|
||||
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_retry: Next request will be sent in %"U32_F" ms\n",
|
||||
sntp_retry_timeout));
|
||||
|
||||
/* set up a timer to send a retry and increase the retry delay */
|
||||
sys_timeout(sntp_retry_timeout, sntp_request, NULL);
|
||||
|
||||
#if SNTP_RETRY_TIMEOUT_EXP
|
||||
{
|
||||
u32_t new_retry_timeout;
|
||||
/* increase the timeout for next retry */
|
||||
new_retry_timeout = sntp_retry_timeout << 1;
|
||||
/* limit to maximum timeout and prevent overflow */
|
||||
if ((new_retry_timeout <= SNTP_RETRY_TIMEOUT_MAX) &&
|
||||
(new_retry_timeout > sntp_retry_timeout)) {
|
||||
sntp_retry_timeout = new_retry_timeout;
|
||||
}
|
||||
}
|
||||
#endif /* SNTP_RETRY_TIMEOUT_EXP */
|
||||
}
|
||||
|
||||
#if SNTP_SUPPORT_MULTIPLE_SERVERS
|
||||
/**
|
||||
* If Kiss-of-Death is received (or another packet parsing error),
|
||||
* try the next server or retry the current server and increase the retry
|
||||
* timeout if only one server is available.
|
||||
* (implicitly, SNTP_MAX_SERVERS > 1)
|
||||
*
|
||||
* @param arg is unused (only necessary to conform to sys_timeout)
|
||||
*/
|
||||
static void
|
||||
sntp_try_next_server(void* arg)
|
||||
{
|
||||
u8_t old_server, i;
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
|
||||
old_server = sntp_current_server;
|
||||
for (i = 0; i < SNTP_MAX_SERVERS - 1; i++) {
|
||||
sntp_current_server++;
|
||||
if (sntp_current_server >= SNTP_MAX_SERVERS) {
|
||||
sntp_current_server = 0;
|
||||
}
|
||||
if (!ip_addr_isany(&sntp_servers[sntp_current_server].addr)
|
||||
#if SNTP_SERVER_DNS
|
||||
|| (sntp_servers[sntp_current_server].name != NULL)
|
||||
#endif
|
||||
) {
|
||||
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_try_next_server: Sending request to server %"U16_F"\n",
|
||||
(u16_t)sntp_current_server));
|
||||
/* new server: reset retry timeout */
|
||||
SNTP_RESET_RETRY_TIMEOUT();
|
||||
/* instantly send a request to the next server */
|
||||
sntp_request(NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* no other valid server found */
|
||||
sntp_current_server = old_server;
|
||||
sntp_retry(NULL);
|
||||
}
|
||||
#else /* SNTP_SUPPORT_MULTIPLE_SERVERS */
|
||||
/* Always retry on error if only one server is supported */
|
||||
#define sntp_try_next_server sntp_retry
|
||||
#endif /* SNTP_SUPPORT_MULTIPLE_SERVERS */
|
||||
|
||||
/** UDP recv callback for the sntp pcb */
|
||||
static void
|
||||
sntp_recv(void *arg, struct udp_pcb* pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
|
||||
{
|
||||
u8_t mode;
|
||||
u8_t stratum;
|
||||
u32_t receive_timestamp[SNTP_RECEIVE_TIME_SIZE];
|
||||
err_t err;
|
||||
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
LWIP_UNUSED_ARG(pcb);
|
||||
|
||||
/* packet received: stop retry timeout */
|
||||
sys_untimeout(sntp_try_next_server, NULL);
|
||||
sys_untimeout(sntp_request, NULL);
|
||||
|
||||
err = ERR_ARG;
|
||||
#if SNTP_CHECK_RESPONSE >= 1
|
||||
/* check server address and port */
|
||||
if (((sntp_opmode != SNTP_OPMODE_POLL) || ip_addr_cmp(addr, &sntp_last_server_address)) &&
|
||||
(port == SNTP_PORT))
|
||||
#else /* SNTP_CHECK_RESPONSE >= 1 */
|
||||
LWIP_UNUSED_ARG(addr);
|
||||
LWIP_UNUSED_ARG(port);
|
||||
#endif /* SNTP_CHECK_RESPONSE >= 1 */
|
||||
{
|
||||
/* process the response */
|
||||
if (p->tot_len == SNTP_MSG_LEN) {
|
||||
pbuf_copy_partial(p, &mode, 1, SNTP_OFFSET_LI_VN_MODE);
|
||||
mode &= SNTP_MODE_MASK;
|
||||
/* if this is a SNTP response... */
|
||||
if (((sntp_opmode == SNTP_OPMODE_POLL) && (mode == SNTP_MODE_SERVER)) ||
|
||||
((sntp_opmode == SNTP_OPMODE_LISTENONLY) && (mode == SNTP_MODE_BROADCAST))) {
|
||||
pbuf_copy_partial(p, &stratum, 1, SNTP_OFFSET_STRATUM);
|
||||
if (stratum == SNTP_STRATUM_KOD) {
|
||||
/* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
|
||||
err = SNTP_ERR_KOD;
|
||||
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Received Kiss-of-Death\n"));
|
||||
} else {
|
||||
#if SNTP_CHECK_RESPONSE >= 2
|
||||
/* check originate_timetamp against sntp_last_timestamp_sent */
|
||||
u32_t originate_timestamp[2];
|
||||
pbuf_copy_partial(p, &originate_timestamp, 8, SNTP_OFFSET_ORIGINATE_TIME);
|
||||
if ((originate_timestamp[0] != sntp_last_timestamp_sent[0]) ||
|
||||
(originate_timestamp[1] != sntp_last_timestamp_sent[1]))
|
||||
{
|
||||
LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid originate timestamp in response\n"));
|
||||
} else
|
||||
#endif /* SNTP_CHECK_RESPONSE >= 2 */
|
||||
/* @todo: add code for SNTP_CHECK_RESPONSE >= 3 and >= 4 here */
|
||||
{
|
||||
/* correct answer */
|
||||
err = ERR_OK;
|
||||
pbuf_copy_partial(p, &receive_timestamp, SNTP_RECEIVE_TIME_SIZE * 4, SNTP_OFFSET_TRANSMIT_TIME);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid mode in response: %"U16_F"\n", (u16_t)mode));
|
||||
/* wait for correct response */
|
||||
err = ERR_TIMEOUT;
|
||||
}
|
||||
} else {
|
||||
LWIP_DEBUGF(SNTP_DEBUG_WARN, ("sntp_recv: Invalid packet length: %"U16_F"\n", p->tot_len));
|
||||
}
|
||||
}
|
||||
#if SNTP_CHECK_RESPONSE >= 1
|
||||
else {
|
||||
/* packet from wrong remote address or port, wait for correct response */
|
||||
err = ERR_TIMEOUT;
|
||||
}
|
||||
#endif /* SNTP_CHECK_RESPONSE >= 1 */
|
||||
pbuf_free(p);
|
||||
if (err == ERR_OK) {
|
||||
sntp_process(receive_timestamp);
|
||||
|
||||
/* Set up timeout for next request (only if poll response was received)*/
|
||||
if (sntp_opmode == SNTP_OPMODE_POLL) {
|
||||
/* Correct response, reset retry timeout */
|
||||
SNTP_RESET_RETRY_TIMEOUT();
|
||||
|
||||
sys_timeout((u32_t)SNTP_UPDATE_DELAY, sntp_request, NULL);
|
||||
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_recv: Scheduled next time request: %"U32_F" ms\n",
|
||||
(u32_t)SNTP_UPDATE_DELAY));
|
||||
}
|
||||
} else if (err != ERR_TIMEOUT) {
|
||||
/* Errors are only processed in case of an explicit poll response */
|
||||
if (sntp_opmode == SNTP_OPMODE_POLL) {
|
||||
if (err == SNTP_ERR_KOD) {
|
||||
/* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
|
||||
sntp_try_next_server(NULL);
|
||||
} else {
|
||||
/* another error, try the same server again */
|
||||
sntp_retry(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Actually send an sntp request to a server.
|
||||
*
|
||||
* @param server_addr resolved IP address of the SNTP server
|
||||
*/
|
||||
static void
|
||||
sntp_send_request(const ip_addr_t *server_addr)
|
||||
{
|
||||
struct pbuf* p;
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, SNTP_MSG_LEN, PBUF_RAM);
|
||||
if (p != NULL) {
|
||||
struct sntp_msg *sntpmsg = (struct sntp_msg *)p->payload;
|
||||
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_send_request: Sending request to server\n"));
|
||||
/* initialize request message */
|
||||
sntp_initialize_request(sntpmsg);
|
||||
/* send request */
|
||||
udp_sendto(sntp_pcb, p, server_addr, SNTP_PORT);
|
||||
/* free the pbuf after sending it */
|
||||
pbuf_free(p);
|
||||
/* set up receive timeout: try next server or retry on timeout */
|
||||
sys_timeout((u32_t)SNTP_RECV_TIMEOUT, sntp_try_next_server, NULL);
|
||||
#if SNTP_CHECK_RESPONSE >= 1
|
||||
/* save server address to verify it in sntp_recv */
|
||||
ip_addr_set(&sntp_last_server_address, server_addr);
|
||||
#endif /* SNTP_CHECK_RESPONSE >= 1 */
|
||||
} else {
|
||||
LWIP_DEBUGF(SNTP_DEBUG_SERIOUS, ("sntp_send_request: Out of memory, trying again in %"U32_F" ms\n",
|
||||
(u32_t)SNTP_RETRY_TIMEOUT));
|
||||
/* out of memory: set up a timer to send a retry */
|
||||
sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_request, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#if SNTP_SERVER_DNS
|
||||
/**
|
||||
* DNS found callback when using DNS names as server address.
|
||||
*/
|
||||
static void
|
||||
sntp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
|
||||
{
|
||||
LWIP_UNUSED_ARG(hostname);
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
|
||||
if (ipaddr != NULL) {
|
||||
/* Address resolved, send request */
|
||||
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_dns_found: Server address resolved, sending request\n"));
|
||||
sntp_send_request(ipaddr);
|
||||
} else {
|
||||
/* DNS resolving failed -> try another server */
|
||||
LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_dns_found: Failed to resolve server address resolved, trying next server\n"));
|
||||
sntp_try_next_server(NULL);
|
||||
}
|
||||
}
|
||||
#endif /* SNTP_SERVER_DNS */
|
||||
|
||||
/**
|
||||
* Send out an sntp request.
|
||||
*
|
||||
* @param arg is unused (only necessary to conform to sys_timeout)
|
||||
*/
|
||||
static void
|
||||
sntp_request(void *arg)
|
||||
{
|
||||
ip_addr_t sntp_server_address;
|
||||
err_t err;
|
||||
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
|
||||
/* initialize SNTP server address */
|
||||
#if SNTP_SERVER_DNS
|
||||
if (sntp_servers[sntp_current_server].name) {
|
||||
/* always resolve the name and rely on dns-internal caching & timeout */
|
||||
ip_addr_set_zero(&sntp_servers[sntp_current_server].addr);
|
||||
err = dns_gethostbyname(sntp_servers[sntp_current_server].name, &sntp_server_address,
|
||||
sntp_dns_found, NULL);
|
||||
if (err == ERR_INPROGRESS) {
|
||||
/* DNS request sent, wait for sntp_dns_found being called */
|
||||
LWIP_DEBUGF(SNTP_DEBUG_STATE, ("sntp_request: Waiting for server address to be resolved.\n"));
|
||||
return;
|
||||
} else if (err == ERR_OK) {
|
||||
sntp_servers[sntp_current_server].addr = sntp_server_address;
|
||||
}
|
||||
} else
|
||||
#endif /* SNTP_SERVER_DNS */
|
||||
{
|
||||
sntp_server_address = sntp_servers[sntp_current_server].addr;
|
||||
err = (ip_addr_isany_val(sntp_server_address)) ? ERR_ARG : ERR_OK;
|
||||
}
|
||||
|
||||
if (err == ERR_OK) {
|
||||
LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_request: current server address is %s\n",
|
||||
ipaddr_ntoa(&sntp_server_address)));
|
||||
sntp_send_request(&sntp_server_address);
|
||||
} else {
|
||||
/* address conversion failed, try another server */
|
||||
LWIP_DEBUGF(SNTP_DEBUG_WARN_STATE, ("sntp_request: Invalid server address, trying next server.\n"));
|
||||
sys_timeout((u32_t)SNTP_RETRY_TIMEOUT, sntp_try_next_server, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup sntp
|
||||
* Initialize this module.
|
||||
* Send out request instantly or after SNTP_STARTUP_DELAY(_FUNC).
|
||||
*/
|
||||
void
|
||||
sntp_init(void)
|
||||
{
|
||||
#ifdef SNTP_SERVER_ADDRESS
|
||||
#if SNTP_SERVER_DNS
|
||||
sntp_setservername(0, SNTP_SERVER_ADDRESS);
|
||||
#else
|
||||
#error SNTP_SERVER_ADDRESS string not supported SNTP_SERVER_DNS==0
|
||||
#endif
|
||||
#endif /* SNTP_SERVER_ADDRESS */
|
||||
|
||||
if (sntp_pcb == NULL) {
|
||||
sntp_pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
|
||||
LWIP_ASSERT("Failed to allocate udp pcb for sntp client", sntp_pcb != NULL);
|
||||
if (sntp_pcb != NULL) {
|
||||
udp_recv(sntp_pcb, sntp_recv, NULL);
|
||||
|
||||
if (sntp_opmode == SNTP_OPMODE_POLL) {
|
||||
SNTP_RESET_RETRY_TIMEOUT();
|
||||
#if SNTP_STARTUP_DELAY
|
||||
sys_timeout((u32_t)SNTP_STARTUP_DELAY_FUNC, sntp_request, NULL);
|
||||
#else
|
||||
sntp_request(NULL);
|
||||
#endif
|
||||
} else if (sntp_opmode == SNTP_OPMODE_LISTENONLY) {
|
||||
ip_set_option(sntp_pcb, SOF_BROADCAST);
|
||||
udp_bind(sntp_pcb, IP_ANY_TYPE, SNTP_PORT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup sntp
|
||||
* Stop this module.
|
||||
*/
|
||||
void
|
||||
sntp_stop(void)
|
||||
{
|
||||
if (sntp_pcb != NULL) {
|
||||
sys_untimeout(sntp_request, NULL);
|
||||
udp_remove(sntp_pcb);
|
||||
sntp_pcb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup sntp
|
||||
* Get enabled state.
|
||||
*/
|
||||
u8_t sntp_enabled(void)
|
||||
{
|
||||
return (sntp_pcb != NULL)? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup sntp
|
||||
* Sets the operating mode.
|
||||
* @param operating_mode one of the available operating modes
|
||||
*/
|
||||
void
|
||||
sntp_setoperatingmode(u8_t operating_mode)
|
||||
{
|
||||
LWIP_ASSERT("Invalid operating mode", operating_mode <= SNTP_OPMODE_LISTENONLY);
|
||||
LWIP_ASSERT("Operating mode must not be set while SNTP client is running", sntp_pcb == NULL);
|
||||
sntp_opmode = operating_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup sntp
|
||||
* Gets the operating mode.
|
||||
*/
|
||||
u8_t
|
||||
sntp_getoperatingmode(void)
|
||||
{
|
||||
return sntp_opmode;
|
||||
}
|
||||
|
||||
#if SNTP_GET_SERVERS_FROM_DHCP
|
||||
/**
|
||||
* Config SNTP server handling by IP address, name, or DHCP; clear table
|
||||
* @param set_servers_from_dhcp enable or disable getting server addresses from dhcp
|
||||
*/
|
||||
void
|
||||
sntp_servermode_dhcp(int set_servers_from_dhcp)
|
||||
{
|
||||
u8_t new_mode = set_servers_from_dhcp ? 1 : 0;
|
||||
if (sntp_set_servers_from_dhcp != new_mode) {
|
||||
sntp_set_servers_from_dhcp = new_mode;
|
||||
}
|
||||
}
|
||||
#endif /* SNTP_GET_SERVERS_FROM_DHCP */
|
||||
|
||||
/**
|
||||
* @ingroup sntp
|
||||
* Initialize one of the NTP servers by IP address
|
||||
*
|
||||
* @param idx the index of the NTP server to set must be < SNTP_MAX_SERVERS
|
||||
* @param server IP address of the NTP server to set
|
||||
*/
|
||||
void
|
||||
sntp_setserver(u8_t idx, const ip_addr_t *server)
|
||||
{
|
||||
if (idx < SNTP_MAX_SERVERS) {
|
||||
if (server != NULL) {
|
||||
sntp_servers[idx].addr = (*server);
|
||||
} else {
|
||||
ip_addr_set_zero(&sntp_servers[idx].addr);
|
||||
}
|
||||
#if SNTP_SERVER_DNS
|
||||
sntp_servers[idx].name = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP
|
||||
/**
|
||||
* Initialize one of the NTP servers by IP address, required by DHCP
|
||||
*
|
||||
* @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS
|
||||
* @param dnsserver IP address of the NTP server to set
|
||||
*/
|
||||
void
|
||||
dhcp_set_ntp_servers(u8_t num, const ip4_addr_t *server)
|
||||
{
|
||||
LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp: %s %u.%u.%u.%u as NTP server #%u via DHCP\n",
|
||||
(sntp_set_servers_from_dhcp ? "Got" : "Rejected"),
|
||||
ip4_addr1(server), ip4_addr2(server), ip4_addr3(server), ip4_addr4(server), num));
|
||||
if (sntp_set_servers_from_dhcp && num) {
|
||||
u8_t i;
|
||||
for (i = 0; (i < num) && (i < SNTP_MAX_SERVERS); i++) {
|
||||
ip_addr_t addr;
|
||||
ip_addr_copy_from_ip4(addr, server[i]);
|
||||
sntp_setserver(i, &addr);
|
||||
}
|
||||
for (i = num; i < SNTP_MAX_SERVERS; i++) {
|
||||
sntp_setserver(i, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* LWIP_DHCP && SNTP_GET_SERVERS_FROM_DHCP */
|
||||
|
||||
/**
|
||||
* @ingroup sntp
|
||||
* Obtain one of the currently configured by IP address (or DHCP) NTP servers
|
||||
*
|
||||
* @param idx the index of the NTP server
|
||||
* @return IP address of the indexed NTP server or "ip_addr_any" if the NTP
|
||||
* server has not been configured by address (or at all).
|
||||
*/
|
||||
const ip_addr_t*
|
||||
sntp_getserver(u8_t idx)
|
||||
{
|
||||
if (idx < SNTP_MAX_SERVERS) {
|
||||
return &sntp_servers[idx].addr;
|
||||
}
|
||||
return IP4_ADDR_ANY;
|
||||
}
|
||||
|
||||
#if SNTP_SERVER_DNS
|
||||
/**
|
||||
* Initialize one of the NTP servers by name
|
||||
*
|
||||
* @param numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS
|
||||
* @param dnsserver DNS name of the NTP server to set, to be resolved at contact time
|
||||
*/
|
||||
void
|
||||
sntp_setservername(u8_t idx, char *server)
|
||||
{
|
||||
if (idx < SNTP_MAX_SERVERS) {
|
||||
sntp_servers[idx].name = server;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain one of the currently configured by name NTP servers.
|
||||
*
|
||||
* @param numdns the index of the NTP server
|
||||
* @return IP address of the indexed NTP server or NULL if the NTP
|
||||
* server has not been configured by name (or at all)
|
||||
*/
|
||||
char *
|
||||
sntp_getservername(u8_t idx)
|
||||
{
|
||||
if (idx < SNTP_MAX_SERVERS) {
|
||||
return sntp_servers[idx].name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif /* SNTP_SERVER_DNS */
|
||||
|
||||
#endif /* LWIP_UDP */
|
||||
417
src/apps/tftp/tftp_server.c
Normal file
417
src/apps/tftp/tftp_server.c
Normal file
@@ -0,0 +1,417 @@
|
||||
/****************************************************************//**
|
||||
*
|
||||
* @file tftp_server.c
|
||||
*
|
||||
* @author Logan Gunthorpe <logang@deltatee.com>
|
||||
* Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
*
|
||||
* @brief Trivial File Transfer Protocol (RFC 1350)
|
||||
*
|
||||
* Copyright (c) Deltatee Enterprises Ltd. 2013
|
||||
* All rights reserved.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
/*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification,are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Logan Gunthorpe <logang@deltatee.com>
|
||||
* Dirk Ziegelmeier <dziegel@gmx.de>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup tftp TFTP server
|
||||
* @ingroup apps
|
||||
*
|
||||
* This is simple TFTP server for the lwIP raw API.
|
||||
*/
|
||||
|
||||
#include "lwip/apps/tftp_server.h"
|
||||
|
||||
#if LWIP_UDP
|
||||
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/timeouts.h"
|
||||
#include "lwip/debug.h"
|
||||
|
||||
#define TFTP_MAX_PAYLOAD_SIZE 512
|
||||
#define TFTP_HEADER_LENGTH 4
|
||||
|
||||
#define TFTP_RRQ 1
|
||||
#define TFTP_WRQ 2
|
||||
#define TFTP_DATA 3
|
||||
#define TFTP_ACK 4
|
||||
#define TFTP_ERROR 5
|
||||
|
||||
enum tftp_error {
|
||||
TFTP_ERROR_FILE_NOT_FOUND = 1,
|
||||
TFTP_ERROR_ACCESS_VIOLATION = 2,
|
||||
TFTP_ERROR_DISK_FULL = 3,
|
||||
TFTP_ERROR_ILLEGAL_OPERATION = 4,
|
||||
TFTP_ERROR_UNKNOWN_TRFR_ID = 5,
|
||||
TFTP_ERROR_FILE_EXISTS = 6,
|
||||
TFTP_ERROR_NO_SUCH_USER = 7
|
||||
};
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct tftp_state {
|
||||
const struct tftp_context *ctx;
|
||||
void *handle;
|
||||
struct pbuf *last_data;
|
||||
struct udp_pcb *upcb;
|
||||
ip_addr_t addr;
|
||||
u16_t port;
|
||||
int timer;
|
||||
int last_pkt;
|
||||
u16_t blknum;
|
||||
u8_t retries;
|
||||
u8_t mode_write;
|
||||
};
|
||||
|
||||
static struct tftp_state tftp_state;
|
||||
|
||||
static void tftp_tmr(void* arg);
|
||||
|
||||
static void
|
||||
close_handle(void)
|
||||
{
|
||||
tftp_state.port = 0;
|
||||
ip_addr_set_any(0, &tftp_state.addr);
|
||||
|
||||
if(tftp_state.last_data != NULL) {
|
||||
pbuf_free(tftp_state.last_data);
|
||||
tftp_state.last_data = NULL;
|
||||
}
|
||||
|
||||
sys_untimeout(tftp_tmr, NULL);
|
||||
|
||||
if (tftp_state.handle) {
|
||||
tftp_state.ctx->close(tftp_state.handle);
|
||||
tftp_state.handle = NULL;
|
||||
LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n"));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str)
|
||||
{
|
||||
int str_length = strlen(str);
|
||||
struct pbuf* p;
|
||||
u16_t* payload;
|
||||
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + str_length + 1), PBUF_RAM);
|
||||
if(p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
payload = (u16_t*) p->payload;
|
||||
payload[0] = PP_HTONS(TFTP_ERROR);
|
||||
payload[1] = lwip_htons(code);
|
||||
MEMCPY(&payload[2], str, str_length + 1);
|
||||
|
||||
udp_sendto(tftp_state.upcb, p, addr, port);
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
static void
|
||||
send_ack(u16_t blknum)
|
||||
{
|
||||
struct pbuf* p;
|
||||
u16_t* payload;
|
||||
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH, PBUF_RAM);
|
||||
if(p == NULL) {
|
||||
return;
|
||||
}
|
||||
payload = (u16_t*) p->payload;
|
||||
|
||||
payload[0] = PP_HTONS(TFTP_ACK);
|
||||
payload[1] = lwip_htons(blknum);
|
||||
udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
static void
|
||||
resend_data(void)
|
||||
{
|
||||
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
|
||||
if(p == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(pbuf_copy(p, tftp_state.last_data) != ERR_OK) {
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
|
||||
udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
static void
|
||||
send_data(void)
|
||||
{
|
||||
u16_t *payload;
|
||||
int ret;
|
||||
|
||||
if(tftp_state.last_data != NULL) {
|
||||
pbuf_free(tftp_state.last_data);
|
||||
}
|
||||
|
||||
tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM);
|
||||
if(tftp_state.last_data == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
payload = (u16_t *) tftp_state.last_data->payload;
|
||||
payload[0] = PP_HTONS(TFTP_DATA);
|
||||
payload[1] = lwip_htons(tftp_state.blknum);
|
||||
|
||||
ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
|
||||
if (ret < 0) {
|
||||
send_error(&tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file.");
|
||||
close_handle();
|
||||
return;
|
||||
}
|
||||
|
||||
pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
|
||||
resend_data();
|
||||
}
|
||||
|
||||
static void
|
||||
recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
|
||||
{
|
||||
u16_t *sbuf = (u16_t *) p->payload;
|
||||
int opcode;
|
||||
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
LWIP_UNUSED_ARG(upcb);
|
||||
|
||||
if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
|
||||
(!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
|
||||
opcode = sbuf[0];
|
||||
|
||||
tftp_state.last_pkt = tftp_state.timer;
|
||||
tftp_state.retries = 0;
|
||||
|
||||
switch (opcode) {
|
||||
case PP_HTONS(TFTP_RRQ): /* fall through */
|
||||
case PP_HTONS(TFTP_WRQ):
|
||||
{
|
||||
const char tftp_null = 0;
|
||||
char filename[TFTP_MAX_FILENAME_LEN];
|
||||
char mode[TFTP_MAX_MODE_LEN];
|
||||
u16_t filename_end_offset;
|
||||
u16_t mode_end_offset;
|
||||
|
||||
if(tftp_state.handle != NULL) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
|
||||
break;
|
||||
}
|
||||
|
||||
sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
|
||||
|
||||
/* find \0 in pbuf -> end of filename string */
|
||||
filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
|
||||
if((u16_t)(filename_end_offset-2) > sizeof(filename)) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
|
||||
break;
|
||||
}
|
||||
pbuf_copy_partial(p, filename, filename_end_offset-2, 2);
|
||||
|
||||
/* find \0 in pbuf -> end of mode string */
|
||||
mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1);
|
||||
if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
|
||||
break;
|
||||
}
|
||||
pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1);
|
||||
|
||||
tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
|
||||
tftp_state.blknum = 1;
|
||||
|
||||
if (!tftp_state.handle) {
|
||||
send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
|
||||
break;
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
|
||||
ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
|
||||
LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
|
||||
|
||||
ip_addr_copy(tftp_state.addr, *addr);
|
||||
tftp_state.port = port;
|
||||
|
||||
if (opcode == PP_HTONS(TFTP_WRQ)) {
|
||||
tftp_state.mode_write = 1;
|
||||
send_ack(0);
|
||||
} else {
|
||||
tftp_state.mode_write = 0;
|
||||
send_data();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PP_HTONS(TFTP_DATA):
|
||||
{
|
||||
int ret;
|
||||
u16_t blknum;
|
||||
|
||||
if (tftp_state.handle == NULL) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
|
||||
break;
|
||||
}
|
||||
|
||||
if (tftp_state.mode_write != 1) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
|
||||
break;
|
||||
}
|
||||
|
||||
blknum = lwip_ntohs(sbuf[1]);
|
||||
pbuf_header(p, -TFTP_HEADER_LENGTH);
|
||||
|
||||
ret = tftp_state.ctx->write(tftp_state.handle, p);
|
||||
if (ret < 0) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
|
||||
close_handle();
|
||||
} else {
|
||||
send_ack(blknum);
|
||||
}
|
||||
|
||||
if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
|
||||
close_handle();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PP_HTONS(TFTP_ACK):
|
||||
{
|
||||
u16_t blknum;
|
||||
int lastpkt;
|
||||
|
||||
if (tftp_state.handle == NULL) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
|
||||
break;
|
||||
}
|
||||
|
||||
if (tftp_state.mode_write != 0) {
|
||||
send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
|
||||
break;
|
||||
}
|
||||
|
||||
blknum = lwip_ntohs(sbuf[1]);
|
||||
if (blknum != tftp_state.blknum) {
|
||||
send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
|
||||
break;
|
||||
}
|
||||
|
||||
lastpkt = 0;
|
||||
|
||||
if (tftp_state.last_data != NULL) {
|
||||
lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
|
||||
}
|
||||
|
||||
if (!lastpkt) {
|
||||
tftp_state.blknum++;
|
||||
send_data();
|
||||
} else {
|
||||
close_handle();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
|
||||
break;
|
||||
}
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
static void
|
||||
tftp_tmr(void* arg)
|
||||
{
|
||||
LWIP_UNUSED_ARG(arg);
|
||||
|
||||
tftp_state.timer++;
|
||||
|
||||
if (tftp_state.handle == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
|
||||
|
||||
if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
|
||||
if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
|
||||
LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
|
||||
resend_data();
|
||||
tftp_state.retries++;
|
||||
} else {
|
||||
LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
|
||||
close_handle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @ingroup tftp
|
||||
* Initialize TFTP server.
|
||||
* @param ctx TFTP callback struct
|
||||
*/
|
||||
err_t
|
||||
tftp_init(const struct tftp_context *ctx)
|
||||
{
|
||||
err_t ret;
|
||||
|
||||
struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
|
||||
if (pcb == NULL) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
|
||||
if (ret != ERR_OK) {
|
||||
udp_remove(pcb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tftp_state.handle = NULL;
|
||||
tftp_state.port = 0;
|
||||
tftp_state.ctx = ctx;
|
||||
tftp_state.timer = 0;
|
||||
tftp_state.last_data = NULL;
|
||||
tftp_state.upcb = pcb;
|
||||
|
||||
udp_recv(pcb, recv, NULL);
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
#endif /* LWIP_UDP */
|
||||
218
src/core/def.c
Normal file
218
src/core/def.c
Normal file
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* @file
|
||||
* Common functions used throughout the stack.
|
||||
*
|
||||
* These are reference implementations of the byte swapping functions.
|
||||
* Again with the aim of being simple, correct and fully portable.
|
||||
* Byte swapping is the second thing you would want to optimize. You will
|
||||
* need to port it to your architecture and in your cc.h:
|
||||
*
|
||||
* \#define lwip_htons(x) your_htons
|
||||
* \#define lwip_htonl(x) your_htonl
|
||||
*
|
||||
* Note lwip_ntohs() and lwip_ntohl() are merely references to the htonx counterparts.
|
||||
*
|
||||
* If you \#define them to htons() and htonl(), you should
|
||||
* \#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS to prevent lwIP from
|
||||
* defining htonx/ntohx compatibility macros.
|
||||
|
||||
* @defgroup sys_nonstandard Non-standard functions
|
||||
* @ingroup sys_layer
|
||||
* lwIP provides default implementations for non-standard functions.
|
||||
* These can be mapped to OS functions to reduce code footprint if desired.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Simon Goldschmidt
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/def.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
|
||||
#if !defined(lwip_htons)
|
||||
/**
|
||||
* Convert an u16_t from host- to network byte order.
|
||||
*
|
||||
* @param n u16_t in host byte order
|
||||
* @return n in network byte order
|
||||
*/
|
||||
u16_t
|
||||
lwip_htons(u16_t n)
|
||||
{
|
||||
return (u16_t)PP_HTONS(n);
|
||||
}
|
||||
#endif /* lwip_htons */
|
||||
|
||||
#if !defined(lwip_htonl)
|
||||
/**
|
||||
* Convert an u32_t from host- to network byte order.
|
||||
*
|
||||
* @param n u32_t in host byte order
|
||||
* @return n in network byte order
|
||||
*/
|
||||
u32_t
|
||||
lwip_htonl(u32_t n)
|
||||
{
|
||||
return (u32_t)PP_HTONL(n);
|
||||
}
|
||||
#endif /* lwip_htonl */
|
||||
|
||||
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
|
||||
|
||||
#ifndef lwip_strnstr
|
||||
/**
|
||||
* @ingroup sys_nonstandard
|
||||
* lwIP default implementation for strnstr() non-standard function.
|
||||
* This can be \#defined to strnstr() depending on your platform port.
|
||||
*/
|
||||
char*
|
||||
lwip_strnstr(const char* buffer, const char* token, size_t n)
|
||||
{
|
||||
const char* p;
|
||||
int tokenlen = (int)strlen(token);
|
||||
if (tokenlen == 0) {
|
||||
return (char *)(size_t)buffer;
|
||||
}
|
||||
for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) {
|
||||
if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) {
|
||||
return (char *)(size_t)p;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef lwip_stricmp
|
||||
/**
|
||||
* @ingroup sys_nonstandard
|
||||
* lwIP default implementation for stricmp() non-standard function.
|
||||
* This can be \#defined to stricmp() depending on your platform port.
|
||||
*/
|
||||
int
|
||||
lwip_stricmp(const char* str1, const char* str2)
|
||||
{
|
||||
char c1, c2;
|
||||
|
||||
do {
|
||||
c1 = *str1++;
|
||||
c2 = *str2++;
|
||||
if (c1 != c2) {
|
||||
char c1_upc = c1 | 0x20;
|
||||
if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
|
||||
/* characters are not equal an one is in the alphabet range:
|
||||
downcase both chars and check again */
|
||||
char c2_upc = c2 | 0x20;
|
||||
if (c1_upc != c2_upc) {
|
||||
/* still not equal */
|
||||
/* don't care for < or > */
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
/* characters are not equal but none is in the alphabet range */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} while (c1 != 0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef lwip_strnicmp
|
||||
/**
|
||||
* @ingroup sys_nonstandard
|
||||
* lwIP default implementation for strnicmp() non-standard function.
|
||||
* This can be \#defined to strnicmp() depending on your platform port.
|
||||
*/
|
||||
int
|
||||
lwip_strnicmp(const char* str1, const char* str2, size_t len)
|
||||
{
|
||||
char c1, c2;
|
||||
|
||||
do {
|
||||
c1 = *str1++;
|
||||
c2 = *str2++;
|
||||
if (c1 != c2) {
|
||||
char c1_upc = c1 | 0x20;
|
||||
if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
|
||||
/* characters are not equal an one is in the alphabet range:
|
||||
downcase both chars and check again */
|
||||
char c2_upc = c2 | 0x20;
|
||||
if (c1_upc != c2_upc) {
|
||||
/* still not equal */
|
||||
/* don't care for < or > */
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
/* characters are not equal but none is in the alphabet range */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} while (len-- && c1 != 0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef lwip_itoa
|
||||
/**
|
||||
* @ingroup sys_nonstandard
|
||||
* lwIP default implementation for itoa() non-standard function.
|
||||
* This can be \#defined to itoa() or snprintf(result, bufsize, "%d", number) depending on your platform port.
|
||||
*/
|
||||
void
|
||||
lwip_itoa(char* result, size_t bufsize, int number)
|
||||
{
|
||||
const int base = 10;
|
||||
char* ptr = result, *ptr1 = result, tmp_char;
|
||||
int tmp_value;
|
||||
LWIP_UNUSED_ARG(bufsize);
|
||||
|
||||
do {
|
||||
tmp_value = number;
|
||||
number /= base;
|
||||
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - number * base)];
|
||||
} while(number);
|
||||
|
||||
/* Apply negative sign */
|
||||
if (tmp_value < 0) {
|
||||
*ptr++ = '-';
|
||||
}
|
||||
*ptr-- = '\0';
|
||||
while(ptr1 < ptr) {
|
||||
tmp_char = *ptr;
|
||||
*ptr--= *ptr1;
|
||||
*ptr1++ = tmp_char;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
1416
src/core/dhcp.c
1416
src/core/dhcp.c
File diff suppressed because it is too large
Load Diff
1433
src/core/dns.c
Normal file
1433
src/core/dns.c
Normal file
File diff suppressed because it is too large
Load Diff
354
src/core/inet.c
354
src/core/inet.c
@@ -1,354 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* inet.c
|
||||
*
|
||||
* Functions common to all TCP/IP modules, such as the Internet checksum and the
|
||||
* byte order functions.
|
||||
*
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#include "lwip/arch.h"
|
||||
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/inet.h"
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
static u16_t
|
||||
lwip_chksum(void *dataptr, int len)
|
||||
{
|
||||
u32_t acc;
|
||||
|
||||
LWIP_DEBUGF(INET_DEBUG, ("lwip_chksum(%p, %d)\n", (void *)dataptr, len));
|
||||
for(acc = 0; len > 1; len -= 2) {
|
||||
/* acc = acc + *((u16_t *)dataptr)++;*/
|
||||
acc += *(u16_t *)dataptr;
|
||||
dataptr = (void *)((u16_t *)dataptr + 1);
|
||||
}
|
||||
|
||||
/* add up any odd byte */
|
||||
if (len == 1) {
|
||||
acc += htons((u16_t)((*(u8_t *)dataptr) & 0xff) << 8);
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet: chksum: odd byte %d\n", (unsigned int)(*(u8_t *)dataptr)));
|
||||
} else {
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet: chksum: no odd byte\n"));
|
||||
}
|
||||
acc = (acc >> 16) + (acc & 0xffffUL);
|
||||
|
||||
if ((acc & 0xffff0000) != 0) {
|
||||
acc = (acc >> 16) + (acc & 0xffffUL);
|
||||
}
|
||||
|
||||
return (u16_t)acc;
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* inet_chksum_pseudo:
|
||||
*
|
||||
* Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
u16_t
|
||||
inet_chksum_pseudo(struct pbuf *p,
|
||||
struct ip_addr *src, struct ip_addr *dest,
|
||||
u8_t proto, u16_t proto_len)
|
||||
{
|
||||
u32_t acc;
|
||||
struct pbuf *q;
|
||||
u8_t swapped;
|
||||
|
||||
acc = 0;
|
||||
swapped = 0;
|
||||
/* iterate through all pbuf in chain */
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
|
||||
(void *)q, (void *)q->next));
|
||||
acc += lwip_chksum(q->payload, q->len);
|
||||
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%lx \n", acc));*/
|
||||
while (acc >> 16) {
|
||||
acc = (acc & 0xffffUL) + (acc >> 16);
|
||||
}
|
||||
if (q->len % 2 != 0) {
|
||||
swapped = 1 - swapped;
|
||||
acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8);
|
||||
}
|
||||
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%lx \n", acc));*/
|
||||
}
|
||||
|
||||
if (swapped) {
|
||||
acc = ((acc & 0xff) << 8) | ((acc & 0xff00UL) >> 8);
|
||||
}
|
||||
acc += (src->addr & 0xffffUL);
|
||||
acc += ((src->addr >> 16) & 0xffffUL);
|
||||
acc += (dest->addr & 0xffffUL);
|
||||
acc += ((dest->addr >> 16) & 0xffffUL);
|
||||
acc += (u32_t)htons((u16_t)proto);
|
||||
acc += (u32_t)htons(proto_len);
|
||||
|
||||
while (acc >> 16) {
|
||||
acc = (acc & 0xffffUL) + (acc >> 16);
|
||||
}
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%lx\n", acc));
|
||||
return ~(acc & 0xffffUL);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* inet_chksum:
|
||||
*
|
||||
* Calculates the Internet checksum over a portion of memory. Used primarely for IP
|
||||
* and ICMP.
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
u16_t
|
||||
inet_chksum(void *dataptr, u16_t len)
|
||||
{
|
||||
u32_t acc;
|
||||
|
||||
acc = lwip_chksum(dataptr, len);
|
||||
while (acc >> 16) {
|
||||
acc = (acc & 0xffff) + (acc >> 16);
|
||||
}
|
||||
return ~(acc & 0xffff);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
u16_t
|
||||
inet_chksum_pbuf(struct pbuf *p)
|
||||
{
|
||||
u32_t acc;
|
||||
struct pbuf *q;
|
||||
u8_t swapped;
|
||||
|
||||
acc = 0;
|
||||
swapped = 0;
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
acc += lwip_chksum(q->payload, q->len);
|
||||
while (acc >> 16) {
|
||||
acc = (acc & 0xffffUL) + (acc >> 16);
|
||||
}
|
||||
if (q->len % 2 != 0) {
|
||||
swapped = 1 - swapped;
|
||||
acc = (acc & 0x00ffUL << 8) | (acc & 0xff00UL >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (swapped) {
|
||||
acc = ((acc & 0x00ffUL) << 8) | ((acc & 0xff00UL) >> 8);
|
||||
}
|
||||
return ~(acc & 0xffffUL);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/*
|
||||
* Ascii internet address interpretation routine.
|
||||
* The value returned is in network order.
|
||||
*/
|
||||
|
||||
/* */
|
||||
/* inet_addr */
|
||||
u32_t inet_addr(const char *cp)
|
||||
{
|
||||
struct in_addr val;
|
||||
|
||||
if (inet_aton(cp, &val)) {
|
||||
return (val.s_addr);
|
||||
}
|
||||
return (INADDR_NONE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether "cp" is a valid ascii representation
|
||||
* of an Internet address and convert to a binary address.
|
||||
* Returns 1 if the address is valid, 0 if not.
|
||||
* This replaces inet_addr, the return value from which
|
||||
* cannot distinguish between failure and a local broadcast address.
|
||||
*/
|
||||
/* */
|
||||
/* inet_aton */
|
||||
int inet_aton(const char *cp, struct in_addr *addr)
|
||||
{
|
||||
u32_t val;
|
||||
int base, n;
|
||||
char c;
|
||||
u32_t parts[4];
|
||||
u32_t* pp = parts;
|
||||
|
||||
c = *cp;
|
||||
for (;;) {
|
||||
/*
|
||||
* Collect number up to ``.''.
|
||||
* Values are specified as for C:
|
||||
* 0x=hex, 0=octal, isdigit=decimal.
|
||||
*/
|
||||
if (!isdigit(c))
|
||||
return (0);
|
||||
val = 0; base = 10;
|
||||
if (c == '0') {
|
||||
c = *++cp;
|
||||
if (c == 'x' || c == 'X')
|
||||
base = 16, c = *++cp;
|
||||
else
|
||||
base = 8;
|
||||
}
|
||||
for (;;) {
|
||||
if (isascii(c) && isdigit(c)) {
|
||||
val = (val * base) + (c - '0');
|
||||
c = *++cp;
|
||||
} else if (base == 16 && isascii(c) && isxdigit(c)) {
|
||||
val = (val << 4) |
|
||||
(c + 10 - (islower(c) ? 'a' : 'A'));
|
||||
c = *++cp;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
if (c == '.') {
|
||||
/*
|
||||
* Internet format:
|
||||
* a.b.c.d
|
||||
* a.b.c (with c treated as 16 bits)
|
||||
* a.b (with b treated as 24 bits)
|
||||
*/
|
||||
if (pp >= parts + 3)
|
||||
return (0);
|
||||
*pp++ = val;
|
||||
c = *++cp;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Check for trailing characters.
|
||||
*/
|
||||
if (c != '\0' && (!isascii(c) || !isspace(c)))
|
||||
return (0);
|
||||
/*
|
||||
* Concoct the address according to
|
||||
* the number of parts specified.
|
||||
*/
|
||||
n = pp - parts + 1;
|
||||
switch (n) {
|
||||
|
||||
case 0:
|
||||
return (0); /* initial nondigit */
|
||||
|
||||
case 1: /* a -- 32 bits */
|
||||
break;
|
||||
|
||||
case 2: /* a.b -- 8.24 bits */
|
||||
if (val > 0xffffff)
|
||||
return (0);
|
||||
val |= parts[0] << 24;
|
||||
break;
|
||||
|
||||
case 3: /* a.b.c -- 8.8.16 bits */
|
||||
if (val > 0xffff)
|
||||
return (0);
|
||||
val |= (parts[0] << 24) | (parts[1] << 16);
|
||||
break;
|
||||
|
||||
case 4: /* a.b.c.d -- 8.8.8.8 bits */
|
||||
if (val > 0xff)
|
||||
return (0);
|
||||
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
|
||||
break;
|
||||
}
|
||||
if (addr)
|
||||
addr->s_addr = htonl(val);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Convert numeric IP address into decimal dotted ASCII representation.
|
||||
* returns ptr to static buffer; not reentrant!
|
||||
*/
|
||||
u8_t *inet_ntoa(u32_t addr)
|
||||
{
|
||||
static u8_t str[16];
|
||||
u8_t inv[3];
|
||||
u8_t *rp;
|
||||
u8_t *ap;
|
||||
u8_t rem;
|
||||
u8_t n;
|
||||
u8_t i;
|
||||
|
||||
rp = str;
|
||||
ap = (u8_t *)&addr;
|
||||
for(n = 0; n < 4; n++) {
|
||||
i = 0;
|
||||
do {
|
||||
rem = *ap % (u8_t)10;
|
||||
*ap /= (u8_t)10;
|
||||
inv[i++] = '0' + rem;
|
||||
} while(*ap);
|
||||
while(i--)
|
||||
*rp++ = inv[i];
|
||||
*rp++ = '.';
|
||||
ap++;
|
||||
}
|
||||
*--rp = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
#ifndef BYTE_ORDER
|
||||
#error BYTE_ORDER is not defined
|
||||
#endif
|
||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
u16_t
|
||||
htons(u16_t n)
|
||||
{
|
||||
return ((n & 0xff) << 8) | ((n & 0xff00) >> 8);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
u16_t
|
||||
ntohs(u16_t n)
|
||||
{
|
||||
return htons(n);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
u32_t
|
||||
htonl(u32_t n)
|
||||
{
|
||||
return ((n & 0xff) << 24) |
|
||||
((n & 0xff00) << 8) |
|
||||
((n & 0xff0000) >> 8) |
|
||||
((n & 0xff000000) >> 24);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
u32_t
|
||||
ntohl(u32_t n)
|
||||
{
|
||||
return htonl(n);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
#endif /* BYTE_ORDER == LITTLE_ENDIAN */
|
||||
168
src/core/inet6.c
168
src/core/inet6.c
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* inet6.c
|
||||
*
|
||||
* Functions common to all TCP/IP modules, such as the Internet checksum and the
|
||||
* byte order functions.
|
||||
*
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/inet.h"
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* chksum:
|
||||
*
|
||||
* Sums up all 16 bit words in a memory portion. Also includes any odd byte.
|
||||
* This function is used by the other checksum functions.
|
||||
*
|
||||
* For now, this is not optimized. Must be optimized for the particular processor
|
||||
* arcitecture on which it is to run. Preferebly coded in assembler.
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
static u32_t
|
||||
chksum(void *dataptr, u16_t len)
|
||||
{
|
||||
u16_t *sdataptr = dataptr;
|
||||
u32_t acc;
|
||||
|
||||
|
||||
for(acc = 0; len > 1; len -= 2) {
|
||||
acc += *sdataptr++;
|
||||
}
|
||||
|
||||
/* add up any odd byte */
|
||||
if (len == 1) {
|
||||
acc += htons((u16_t)(*(u8_t *)dataptr) << 8);
|
||||
}
|
||||
|
||||
return acc;
|
||||
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* inet_chksum_pseudo:
|
||||
*
|
||||
* Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
u16_t
|
||||
inet_chksum_pseudo(struct pbuf *p,
|
||||
struct ip_addr *src, struct ip_addr *dest,
|
||||
u8_t proto, u32_t proto_len)
|
||||
{
|
||||
u32_t acc;
|
||||
struct pbuf *q;
|
||||
u8_t swapped, i;
|
||||
|
||||
acc = 0;
|
||||
swapped = 0;
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
acc += chksum(q->payload, q->len);
|
||||
while (acc >> 16) {
|
||||
acc = (acc & 0xffff) + (acc >> 16);
|
||||
}
|
||||
if (q->len % 2 != 0) {
|
||||
swapped = 1 - swapped;
|
||||
acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (swapped) {
|
||||
acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8);
|
||||
}
|
||||
|
||||
for(i = 0; i < 8; i++) {
|
||||
acc += ((u16_t *)src->addr)[i] & 0xffff;
|
||||
acc += ((u16_t *)dest->addr)[i] & 0xffff;
|
||||
while (acc >> 16) {
|
||||
acc = (acc & 0xffff) + (acc >> 16);
|
||||
}
|
||||
}
|
||||
acc += (u16_t)htons((u16_t)proto);
|
||||
acc += ((u16_t *)&proto_len)[0] & 0xffff;
|
||||
acc += ((u16_t *)&proto_len)[1] & 0xffff;
|
||||
|
||||
while (acc >> 16) {
|
||||
acc = (acc & 0xffff) + (acc >> 16);
|
||||
}
|
||||
return ~(acc & 0xffff);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* inet_chksum:
|
||||
*
|
||||
* Calculates the Internet checksum over a portion of memory. Used primarely for IP
|
||||
* and ICMP.
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
u16_t
|
||||
inet_chksum(void *dataptr, u16_t len)
|
||||
{
|
||||
u32_t acc, sum;
|
||||
|
||||
acc = chksum(dataptr, len);
|
||||
sum = (acc & 0xffff) + (acc >> 16);
|
||||
sum += (sum >> 16);
|
||||
return ~(sum & 0xffff);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
u16_t
|
||||
inet_chksum_pbuf(struct pbuf *p)
|
||||
{
|
||||
u32_t acc;
|
||||
struct pbuf *q;
|
||||
u8_t swapped;
|
||||
|
||||
acc = 0;
|
||||
swapped = 0;
|
||||
for(q = p; q != NULL; q = q->next) {
|
||||
acc += chksum(q->payload, q->len);
|
||||
while (acc >> 16) {
|
||||
acc = (acc & 0xffff) + (acc >> 16);
|
||||
}
|
||||
if (q->len % 2 != 0) {
|
||||
swapped = 1 - swapped;
|
||||
acc = (acc & 0xff << 8) | (acc & 0xff00 >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (swapped) {
|
||||
acc = ((acc & 0xff) << 8) | ((acc & 0xff00) >> 8);
|
||||
}
|
||||
return ~(acc & 0xffff);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
610
src/core/inet_chksum.c
Normal file
610
src/core/inet_chksum.c
Normal file
@@ -0,0 +1,610 @@
|
||||
/**
|
||||
* @file
|
||||
* Incluse internet checksum functions.\n
|
||||
*
|
||||
* These are some reference implementations of the checksum algorithm, with the
|
||||
* aim of being simple, correct and fully portable. Checksumming is the
|
||||
* first thing you would want to optimize for your platform. If you create
|
||||
* your own version, link it in and in your cc.h put:
|
||||
*
|
||||
* \#define LWIP_CHKSUM your_checksum_routine
|
||||
*
|
||||
* Or you can select from the implementations below by defining
|
||||
* LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef LWIP_CHKSUM
|
||||
# define LWIP_CHKSUM lwip_standard_chksum
|
||||
# ifndef LWIP_CHKSUM_ALGORITHM
|
||||
# define LWIP_CHKSUM_ALGORITHM 2
|
||||
# endif
|
||||
u16_t lwip_standard_chksum(const void *dataptr, int len);
|
||||
#endif
|
||||
/* If none set: */
|
||||
#ifndef LWIP_CHKSUM_ALGORITHM
|
||||
# define LWIP_CHKSUM_ALGORITHM 0
|
||||
#endif
|
||||
|
||||
#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
|
||||
/**
|
||||
* lwip checksum
|
||||
*
|
||||
* @param dataptr points to start of data to be summed at any boundary
|
||||
* @param len length of data to be summed
|
||||
* @return host order (!) lwip checksum (non-inverted Internet sum)
|
||||
*
|
||||
* @note accumulator size limits summable length to 64k
|
||||
* @note host endianess is irrelevant (p3 RFC1071)
|
||||
*/
|
||||
u16_t
|
||||
lwip_standard_chksum(const void *dataptr, int len)
|
||||
{
|
||||
u32_t acc;
|
||||
u16_t src;
|
||||
const u8_t *octetptr;
|
||||
|
||||
acc = 0;
|
||||
/* dataptr may be at odd or even addresses */
|
||||
octetptr = (const u8_t*)dataptr;
|
||||
while (len > 1) {
|
||||
/* declare first octet as most significant
|
||||
thus assume network order, ignoring host order */
|
||||
src = (*octetptr) << 8;
|
||||
octetptr++;
|
||||
/* declare second octet as least significant */
|
||||
src |= (*octetptr);
|
||||
octetptr++;
|
||||
acc += src;
|
||||
len -= 2;
|
||||
}
|
||||
if (len > 0) {
|
||||
/* accumulate remaining octet */
|
||||
src = (*octetptr) << 8;
|
||||
acc += src;
|
||||
}
|
||||
/* add deferred carry bits */
|
||||
acc = (acc >> 16) + (acc & 0x0000ffffUL);
|
||||
if ((acc & 0xffff0000UL) != 0) {
|
||||
acc = (acc >> 16) + (acc & 0x0000ffffUL);
|
||||
}
|
||||
/* This maybe a little confusing: reorder sum using lwip_htons()
|
||||
instead of lwip_ntohs() since it has a little less call overhead.
|
||||
The caller must invert bits for Internet sum ! */
|
||||
return lwip_htons((u16_t)acc);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
|
||||
/*
|
||||
* Curt McDowell
|
||||
* Broadcom Corp.
|
||||
* csm@broadcom.com
|
||||
*
|
||||
* IP checksum two bytes at a time with support for
|
||||
* unaligned buffer.
|
||||
* Works for len up to and including 0x20000.
|
||||
* by Curt McDowell, Broadcom Corp. 12/08/2005
|
||||
*
|
||||
* @param dataptr points to start of data to be summed at any boundary
|
||||
* @param len length of data to be summed
|
||||
* @return host order (!) lwip checksum (non-inverted Internet sum)
|
||||
*/
|
||||
u16_t
|
||||
lwip_standard_chksum(const void *dataptr, int len)
|
||||
{
|
||||
const u8_t *pb = (const u8_t *)dataptr;
|
||||
const u16_t *ps;
|
||||
u16_t t = 0;
|
||||
u32_t sum = 0;
|
||||
int odd = ((mem_ptr_t)pb & 1);
|
||||
|
||||
/* Get aligned to u16_t */
|
||||
if (odd && len > 0) {
|
||||
((u8_t *)&t)[1] = *pb++;
|
||||
len--;
|
||||
}
|
||||
|
||||
/* Add the bulk of the data */
|
||||
ps = (const u16_t *)(const void *)pb;
|
||||
while (len > 1) {
|
||||
sum += *ps++;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
/* Consume left-over byte, if any */
|
||||
if (len > 0) {
|
||||
((u8_t *)&t)[0] = *(const u8_t *)ps;
|
||||
}
|
||||
|
||||
/* Add end bytes */
|
||||
sum += t;
|
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
calling this twice is probably faster than if statements... */
|
||||
sum = FOLD_U32T(sum);
|
||||
sum = FOLD_U32T(sum);
|
||||
|
||||
/* Swap if alignment was odd */
|
||||
if (odd) {
|
||||
sum = SWAP_BYTES_IN_WORD(sum);
|
||||
}
|
||||
|
||||
return (u16_t)sum;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
|
||||
/**
|
||||
* An optimized checksum routine. Basically, it uses loop-unrolling on
|
||||
* the checksum loop, treating the head and tail bytes specially, whereas
|
||||
* the inner loop acts on 8 bytes at a time.
|
||||
*
|
||||
* @arg start of buffer to be checksummed. May be an odd byte address.
|
||||
* @len number of bytes in the buffer to be checksummed.
|
||||
* @return host order (!) lwip checksum (non-inverted Internet sum)
|
||||
*
|
||||
* by Curt McDowell, Broadcom Corp. December 8th, 2005
|
||||
*/
|
||||
u16_t
|
||||
lwip_standard_chksum(const void *dataptr, int len)
|
||||
{
|
||||
const u8_t *pb = (const u8_t *)dataptr;
|
||||
const u16_t *ps;
|
||||
u16_t t = 0;
|
||||
const u32_t *pl;
|
||||
u32_t sum = 0, tmp;
|
||||
/* starts at odd byte address? */
|
||||
int odd = ((mem_ptr_t)pb & 1);
|
||||
|
||||
if (odd && len > 0) {
|
||||
((u8_t *)&t)[1] = *pb++;
|
||||
len--;
|
||||
}
|
||||
|
||||
ps = (const u16_t *)(const void*)pb;
|
||||
|
||||
if (((mem_ptr_t)ps & 3) && len > 1) {
|
||||
sum += *ps++;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
pl = (const u32_t *)(const void*)ps;
|
||||
|
||||
while (len > 7) {
|
||||
tmp = sum + *pl++; /* ping */
|
||||
if (tmp < sum) {
|
||||
tmp++; /* add back carry */
|
||||
}
|
||||
|
||||
sum = tmp + *pl++; /* pong */
|
||||
if (sum < tmp) {
|
||||
sum++; /* add back carry */
|
||||
}
|
||||
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
/* make room in upper bits */
|
||||
sum = FOLD_U32T(sum);
|
||||
|
||||
ps = (const u16_t *)pl;
|
||||
|
||||
/* 16-bit aligned word remaining? */
|
||||
while (len > 1) {
|
||||
sum += *ps++;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
/* dangling tail byte remaining? */
|
||||
if (len > 0) { /* include odd byte */
|
||||
((u8_t *)&t)[0] = *(const u8_t *)ps;
|
||||
}
|
||||
|
||||
sum += t; /* add end bytes */
|
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
calling this twice is probably faster than if statements... */
|
||||
sum = FOLD_U32T(sum);
|
||||
sum = FOLD_U32T(sum);
|
||||
|
||||
if (odd) {
|
||||
sum = SWAP_BYTES_IN_WORD(sum);
|
||||
}
|
||||
|
||||
return (u16_t)sum;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
|
||||
static u16_t
|
||||
inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
|
||||
{
|
||||
struct pbuf *q;
|
||||
u8_t swapped = 0;
|
||||
|
||||
/* iterate through all pbuf in chain */
|
||||
for (q = p; q != NULL; q = q->next) {
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
|
||||
(void *)q, (void *)q->next));
|
||||
acc += LWIP_CHKSUM(q->payload, q->len);
|
||||
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
|
||||
/* just executing this next line is probably faster that the if statement needed
|
||||
to check whether we really need to execute it, and does no harm */
|
||||
acc = FOLD_U32T(acc);
|
||||
if (q->len % 2 != 0) {
|
||||
swapped = 1 - swapped;
|
||||
acc = SWAP_BYTES_IN_WORD(acc);
|
||||
}
|
||||
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
|
||||
}
|
||||
|
||||
if (swapped) {
|
||||
acc = SWAP_BYTES_IN_WORD(acc);
|
||||
}
|
||||
|
||||
acc += (u32_t)lwip_htons((u16_t)proto);
|
||||
acc += (u32_t)lwip_htons(proto_len);
|
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
calling this twice is probably faster than if statements... */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
|
||||
return (u16_t)~(acc & 0xffffUL);
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
/* inet_chksum_pseudo:
|
||||
*
|
||||
* Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
* IP addresses are expected to be in network byte order.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param src source ip address (used for checksum of pseudo header)
|
||||
* @param dst destination ip address (used for checksum of pseudo header)
|
||||
* @param proto ip protocol (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ip data part (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
const ip4_addr_t *src, const ip4_addr_t *dest)
|
||||
{
|
||||
u32_t acc;
|
||||
u32_t addr;
|
||||
|
||||
addr = ip4_addr_get_u32(src);
|
||||
acc = (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
addr = ip4_addr_get_u32(dest);
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
/* fold down to 16 bits */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
|
||||
return inet_cksum_pseudo_base(p, proto, proto_len, acc);
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
#if LWIP_IPV6
|
||||
/**
|
||||
* Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
|
||||
* IPv6 addresses are expected to be in network byte order.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param proto ipv6 protocol/next header (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
|
||||
* @param src source ipv6 address (used for checksum of pseudo header)
|
||||
* @param dest destination ipv6 address (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
const ip6_addr_t *src, const ip6_addr_t *dest)
|
||||
{
|
||||
u32_t acc = 0;
|
||||
u32_t addr;
|
||||
u8_t addr_part;
|
||||
|
||||
for (addr_part = 0; addr_part < 4; addr_part++) {
|
||||
addr = src->addr[addr_part];
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
addr = dest->addr[addr_part];
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
}
|
||||
/* fold down to 16 bits */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
|
||||
return inet_cksum_pseudo_base(p, proto, proto_len, acc);
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
/* ip_chksum_pseudo:
|
||||
*
|
||||
* Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
* IP addresses are expected to be in network byte order.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param src source ip address (used for checksum of pseudo header)
|
||||
* @param dst destination ip address (used for checksum of pseudo header)
|
||||
* @param proto ip protocol (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ip data part (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
ip_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
const ip_addr_t *src, const ip_addr_t *dest)
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
if (IP_IS_V6(dest)) {
|
||||
return ip6_chksum_pseudo(p, proto, proto_len, ip_2_ip6(src), ip_2_ip6(dest));
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
else
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
#if LWIP_IPV4
|
||||
{
|
||||
return inet_chksum_pseudo(p, proto, proto_len, ip_2_ip4(src), ip_2_ip4(dest));
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
}
|
||||
|
||||
/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
|
||||
static u16_t
|
||||
inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
u16_t chksum_len, u32_t acc)
|
||||
{
|
||||
struct pbuf *q;
|
||||
u8_t swapped = 0;
|
||||
u16_t chklen;
|
||||
|
||||
/* iterate through all pbuf in chain */
|
||||
for (q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
|
||||
(void *)q, (void *)q->next));
|
||||
chklen = q->len;
|
||||
if (chklen > chksum_len) {
|
||||
chklen = chksum_len;
|
||||
}
|
||||
acc += LWIP_CHKSUM(q->payload, chklen);
|
||||
chksum_len -= chklen;
|
||||
LWIP_ASSERT("delete me", chksum_len < 0x7fff);
|
||||
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
|
||||
/* fold the upper bit down */
|
||||
acc = FOLD_U32T(acc);
|
||||
if (q->len % 2 != 0) {
|
||||
swapped = 1 - swapped;
|
||||
acc = SWAP_BYTES_IN_WORD(acc);
|
||||
}
|
||||
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
|
||||
}
|
||||
|
||||
if (swapped) {
|
||||
acc = SWAP_BYTES_IN_WORD(acc);
|
||||
}
|
||||
|
||||
acc += (u32_t)lwip_htons((u16_t)proto);
|
||||
acc += (u32_t)lwip_htons(proto_len);
|
||||
|
||||
/* Fold 32-bit sum to 16 bits
|
||||
calling this twice is probably faster than if statements... */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
|
||||
return (u16_t)~(acc & 0xffffUL);
|
||||
}
|
||||
|
||||
#if LWIP_IPV4
|
||||
/* inet_chksum_pseudo_partial:
|
||||
*
|
||||
* Calculates the IPv4 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
* IP addresses are expected to be in network byte order.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param src source ip address (used for checksum of pseudo header)
|
||||
* @param dst destination ip address (used for checksum of pseudo header)
|
||||
* @param proto ip protocol (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ip data part (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
u16_t chksum_len, const ip4_addr_t *src, const ip4_addr_t *dest)
|
||||
{
|
||||
u32_t acc;
|
||||
u32_t addr;
|
||||
|
||||
addr = ip4_addr_get_u32(src);
|
||||
acc = (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
addr = ip4_addr_get_u32(dest);
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
/* fold down to 16 bits */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
|
||||
return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
|
||||
#if LWIP_IPV6
|
||||
/**
|
||||
* Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
|
||||
* IPv6 addresses are expected to be in network byte order. Will only compute for a
|
||||
* portion of the payload.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param proto ipv6 protocol/next header (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
|
||||
* @param chksum_len number of payload bytes used to compute chksum
|
||||
* @param src source ipv6 address (used for checksum of pseudo header)
|
||||
* @param dest destination ipv6 address (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest)
|
||||
{
|
||||
u32_t acc = 0;
|
||||
u32_t addr;
|
||||
u8_t addr_part;
|
||||
|
||||
for (addr_part = 0; addr_part < 4; addr_part++) {
|
||||
addr = src->addr[addr_part];
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
addr = dest->addr[addr_part];
|
||||
acc += (addr & 0xffffUL);
|
||||
acc += ((addr >> 16) & 0xffffUL);
|
||||
}
|
||||
/* fold down to 16 bits */
|
||||
acc = FOLD_U32T(acc);
|
||||
acc = FOLD_U32T(acc);
|
||||
|
||||
return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
/* ip_chksum_pseudo_partial:
|
||||
*
|
||||
* Calculates the IPv4 or IPv6 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
|
||||
*
|
||||
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
|
||||
* @param src source ip address (used for checksum of pseudo header)
|
||||
* @param dst destination ip address (used for checksum of pseudo header)
|
||||
* @param proto ip protocol (used for checksum of pseudo header)
|
||||
* @param proto_len length of the ip data part (used for checksum of pseudo header)
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
ip_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
|
||||
u16_t chksum_len, const ip_addr_t *src, const ip_addr_t *dest)
|
||||
{
|
||||
#if LWIP_IPV6
|
||||
if (IP_IS_V6(dest)) {
|
||||
return ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip6(src), ip_2_ip6(dest));
|
||||
}
|
||||
#endif /* LWIP_IPV6 */
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
else
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
#if LWIP_IPV4
|
||||
{
|
||||
return inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ip_2_ip4(src), ip_2_ip4(dest));
|
||||
}
|
||||
#endif /* LWIP_IPV4 */
|
||||
}
|
||||
|
||||
/* inet_chksum:
|
||||
*
|
||||
* Calculates the Internet checksum over a portion of memory. Used primarily for IP
|
||||
* and ICMP.
|
||||
*
|
||||
* @param dataptr start of the buffer to calculate the checksum (no alignment needed)
|
||||
* @param len length of the buffer to calculate the checksum
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
|
||||
u16_t
|
||||
inet_chksum(const void *dataptr, u16_t len)
|
||||
{
|
||||
return (u16_t)~(unsigned int)LWIP_CHKSUM(dataptr, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a checksum over a chain of pbufs (without pseudo-header, much like
|
||||
* inet_chksum only pbufs are used).
|
||||
*
|
||||
* @param p pbuf chain over that the checksum should be calculated
|
||||
* @return checksum (as u16_t) to be saved directly in the protocol header
|
||||
*/
|
||||
u16_t
|
||||
inet_chksum_pbuf(struct pbuf *p)
|
||||
{
|
||||
u32_t acc;
|
||||
struct pbuf *q;
|
||||
u8_t swapped;
|
||||
|
||||
acc = 0;
|
||||
swapped = 0;
|
||||
for (q = p; q != NULL; q = q->next) {
|
||||
acc += LWIP_CHKSUM(q->payload, q->len);
|
||||
acc = FOLD_U32T(acc);
|
||||
if (q->len % 2 != 0) {
|
||||
swapped = 1 - swapped;
|
||||
acc = SWAP_BYTES_IN_WORD(acc);
|
||||
}
|
||||
}
|
||||
|
||||
if (swapped) {
|
||||
acc = SWAP_BYTES_IN_WORD(acc);
|
||||
}
|
||||
return (u16_t)~(acc & 0xffffUL);
|
||||
}
|
||||
|
||||
/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
|
||||
* like MEMCPY but generates a checksum at the same time. Since this is a
|
||||
* performance-sensitive function, you might want to create your own version
|
||||
* in assembly targeted at your hardware by defining it in lwipopts.h:
|
||||
* #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
|
||||
*/
|
||||
|
||||
#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
|
||||
/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
|
||||
* For architectures with big caches, data might still be in cache when
|
||||
* generating the checksum after copying.
|
||||
*/
|
||||
u16_t
|
||||
lwip_chksum_copy(void *dst, const void *src, u16_t len)
|
||||
{
|
||||
MEMCPY(dst, src, len);
|
||||
return LWIP_CHKSUM(dst, len);
|
||||
}
|
||||
#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
|
||||
383
src/core/init.c
Normal file
383
src/core/init.c
Normal file
@@ -0,0 +1,383 @@
|
||||
/**
|
||||
* @file
|
||||
* Modules initialization
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#include "lwip/init.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/raw.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/priv/tcp_priv.h"
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/timeouts.h"
|
||||
#include "lwip/etharp.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/nd6.h"
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/api.h"
|
||||
|
||||
#include "netif/ppp/ppp_opts.h"
|
||||
#include "netif/ppp/ppp_impl.h"
|
||||
|
||||
#ifndef LWIP_SKIP_PACKING_CHECK
|
||||
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct packed_struct_test
|
||||
{
|
||||
PACK_STRUCT_FLD_8(u8_t dummy1);
|
||||
PACK_STRUCT_FIELD(u32_t dummy2);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
#define PACKED_STRUCT_TEST_EXPECTED_SIZE 5
|
||||
|
||||
#endif
|
||||
|
||||
/* Compile-time sanity checks for configuration errors.
|
||||
* These can be done independently of LWIP_DEBUG, without penalty.
|
||||
*/
|
||||
#ifndef BYTE_ORDER
|
||||
#error "BYTE_ORDER is not defined, you have to define it in your cc.h"
|
||||
#endif
|
||||
#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV)
|
||||
#error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (!LWIP_UDP && LWIP_UDPLITE)
|
||||
#error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (!LWIP_UDP && LWIP_DHCP)
|
||||
#error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (!LWIP_UDP && LWIP_MULTICAST_TX_OPTIONS)
|
||||
#error "If you want to use IGMP/LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (!LWIP_UDP && LWIP_DNS)
|
||||
#error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */
|
||||
#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0))
|
||||
#error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0))
|
||||
#error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0))
|
||||
#error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0))
|
||||
#error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1))
|
||||
#error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_IGMP && !LWIP_MULTICAST_TX_OPTIONS)
|
||||
#error "If you want to use IGMP, you have to define LWIP_MULTICAST_TX_OPTIONS==1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_IGMP && !LWIP_IPV4)
|
||||
#error "IGMP needs LWIP_IPV4 enabled in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_MULTICAST_TX_OPTIONS && !LWIP_IPV4)
|
||||
#error "LWIP_MULTICAST_TX_OPTIONS needs LWIP_IPV4 enabled in your lwipopts.h"
|
||||
#endif
|
||||
#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
|
||||
#error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
|
||||
#endif
|
||||
/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
|
||||
#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0)))
|
||||
#error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
|
||||
#endif
|
||||
#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
|
||||
#error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!"
|
||||
#endif
|
||||
#endif /* !MEMP_MEM_MALLOC */
|
||||
#if LWIP_WND_SCALE
|
||||
#if (LWIP_TCP && (TCP_WND > 0xffffffff))
|
||||
#error "If you want to use TCP, TCP_WND must fit in an u32_t, so, you have to reduce it in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_TCP && LWIP_WND_SCALE && (TCP_RCV_SCALE > 14))
|
||||
#error "The maximum valid window scale value is 14!"
|
||||
#endif
|
||||
#if (LWIP_TCP && (TCP_WND > (0xFFFFU << TCP_RCV_SCALE)))
|
||||
#error "TCP_WND is bigger than the configured LWIP_WND_SCALE allows!"
|
||||
#endif
|
||||
#if (LWIP_TCP && ((TCP_WND >> TCP_RCV_SCALE) == 0))
|
||||
#error "TCP_WND is too small for the configured LWIP_WND_SCALE (results in zero window)!"
|
||||
#endif
|
||||
#else /* LWIP_WND_SCALE */
|
||||
#if (LWIP_TCP && (TCP_WND > 0xffff))
|
||||
#error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h (or enable window scaling)"
|
||||
#endif
|
||||
#endif /* LWIP_WND_SCALE */
|
||||
#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff))
|
||||
#error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2))
|
||||
#error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work"
|
||||
#endif
|
||||
#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12)))
|
||||
#error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_TCP && TCP_LISTEN_BACKLOG && ((TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff)))
|
||||
#error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
|
||||
#endif
|
||||
#if (LWIP_NETIF_API && (NO_SYS==1))
|
||||
#error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h"
|
||||
#endif
|
||||
#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1))
|
||||
#error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_PPP_API && (NO_SYS==1))
|
||||
#error "If you want to use PPP API, you have to define NO_SYS=0 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_PPP_API && (PPP_SUPPORT==0))
|
||||
#error "If you want to use PPP API, you have to enable PPP_SUPPORT in your lwipopts.h"
|
||||
#endif
|
||||
#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
|
||||
#error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK)
|
||||
#error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (!LWIP_ARP && LWIP_AUTOIP)
|
||||
#error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
|
||||
#endif
|
||||
#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API)))
|
||||
#error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h"
|
||||
#endif
|
||||
#if (MEM_LIBC_MALLOC && MEM_USE_POOLS)
|
||||
#error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h"
|
||||
#endif
|
||||
#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS)
|
||||
#error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h"
|
||||
#endif
|
||||
#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT)
|
||||
#error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf"
|
||||
#endif
|
||||
#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT)))
|
||||
#error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
|
||||
#endif
|
||||
#if PPP_SUPPORT && !PPPOS_SUPPORT && !PPPOE_SUPPORT && !PPPOL2TP_SUPPORT
|
||||
#error "PPP_SUPPORT needs at least one of PPPOS_SUPPORT, PPPOE_SUPPORT or PPPOL2TP_SUPPORT turned on"
|
||||
#endif
|
||||
#if PPP_SUPPORT && !PPP_IPV4_SUPPORT && !PPP_IPV6_SUPPORT
|
||||
#error "PPP_SUPPORT needs PPP_IPV4_SUPPORT and/or PPP_IPV6_SUPPORT turned on"
|
||||
#endif
|
||||
#if PPP_SUPPORT && PPP_IPV4_SUPPORT && !LWIP_IPV4
|
||||
#error "PPP_IPV4_SUPPORT needs LWIP_IPV4 turned on"
|
||||
#endif
|
||||
#if PPP_SUPPORT && PPP_IPV6_SUPPORT && !LWIP_IPV6
|
||||
#error "PPP_IPV6_SUPPORT needs LWIP_IPV6 turned on"
|
||||
#endif
|
||||
#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
|
||||
#error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
|
||||
#endif
|
||||
#if (LWIP_IGMP || LWIP_IPV6) && !defined(LWIP_RAND)
|
||||
#error "When using IGMP or IPv6, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value (in arch/cc.h)"
|
||||
#endif
|
||||
#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING
|
||||
#error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
|
||||
#endif
|
||||
#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE
|
||||
#error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets"
|
||||
#endif
|
||||
#if LWIP_NETCONN && LWIP_TCP
|
||||
#if NETCONN_COPY != TCP_WRITE_FLAG_COPY
|
||||
#error "NETCONN_COPY != TCP_WRITE_FLAG_COPY"
|
||||
#endif
|
||||
#if NETCONN_MORE != TCP_WRITE_FLAG_MORE
|
||||
#error "NETCONN_MORE != TCP_WRITE_FLAG_MORE"
|
||||
#endif
|
||||
#endif /* LWIP_NETCONN && LWIP_TCP */
|
||||
#if LWIP_SOCKET
|
||||
/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */
|
||||
#if SO_REUSEADDR != SOF_REUSEADDR
|
||||
#error "WARNING: SO_REUSEADDR != SOF_REUSEADDR"
|
||||
#endif
|
||||
#if SO_KEEPALIVE != SOF_KEEPALIVE
|
||||
#error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE"
|
||||
#endif
|
||||
#if SO_BROADCAST != SOF_BROADCAST
|
||||
#error "WARNING: SO_BROADCAST != SOF_BROADCAST"
|
||||
#endif
|
||||
#endif /* LWIP_SOCKET */
|
||||
|
||||
|
||||
/* Compile-time checks for deprecated options.
|
||||
*/
|
||||
#ifdef MEMP_NUM_TCPIP_MSG
|
||||
#error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
|
||||
#endif
|
||||
#ifdef TCP_REXMIT_DEBUG
|
||||
#error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h."
|
||||
#endif
|
||||
#ifdef RAW_STATS
|
||||
#error "RAW_STATS option is deprecated. Remove it from your lwipopts.h."
|
||||
#endif
|
||||
#ifdef ETHARP_QUEUE_FIRST
|
||||
#error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h."
|
||||
#endif
|
||||
#ifdef ETHARP_ALWAYS_INSERT
|
||||
#error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h."
|
||||
#endif
|
||||
#if !NO_SYS && LWIP_TCPIP_CORE_LOCKING && LWIP_COMPAT_MUTEX && !defined(LWIP_COMPAT_MUTEX_ALLOWED)
|
||||
#error "LWIP_COMPAT_MUTEX cannot prevent priority inversion. It is recommended to implement priority-aware mutexes. (Define LWIP_COMPAT_MUTEX_ALLOWED to disable this error.)"
|
||||
#endif
|
||||
|
||||
#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS
|
||||
#define LWIP_DISABLE_TCP_SANITY_CHECKS 0
|
||||
#endif
|
||||
#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS
|
||||
#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0
|
||||
#endif
|
||||
|
||||
/* MEMP sanity checks */
|
||||
#if MEMP_MEM_MALLOC
|
||||
#if !LWIP_DISABLE_MEMP_SANITY_CHECKS
|
||||
#if LWIP_NETCONN || LWIP_SOCKET
|
||||
#if !MEMP_NUM_NETCONN && LWIP_SOCKET
|
||||
#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!"
|
||||
#endif
|
||||
#else /* MEMP_MEM_MALLOC */
|
||||
#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)
|
||||
#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#endif /* LWIP_NETCONN || LWIP_SOCKET */
|
||||
#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */
|
||||
#if MEM_USE_POOLS
|
||||
#error "MEMP_MEM_MALLOC and MEM_USE_POOLS cannot be enabled at the same time"
|
||||
#endif
|
||||
#ifdef LWIP_HOOK_MEMP_AVAILABLE
|
||||
#error "LWIP_HOOK_MEMP_AVAILABLE doesn't make sense with MEMP_MEM_MALLOC"
|
||||
#endif
|
||||
#endif /* MEMP_MEM_MALLOC */
|
||||
|
||||
/* TCP sanity checks */
|
||||
#if !LWIP_DISABLE_TCP_SANITY_CHECKS
|
||||
#if LWIP_TCP
|
||||
#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN)
|
||||
#error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#if TCP_SND_BUF < (2 * TCP_MSS)
|
||||
#error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS))
|
||||
#error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#if TCP_SNDLOWAT >= TCP_SND_BUF
|
||||
#error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#if TCP_SNDLOWAT >= (0xFFFF - (4 * TCP_MSS))
|
||||
#error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must at least be 4*MSS below u16_t overflow!"
|
||||
#endif
|
||||
#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN
|
||||
#error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
|
||||
#error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#if !MEMP_MEM_MALLOC && PBUF_POOL_SIZE && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))))
|
||||
#error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#if TCP_WND < TCP_MSS
|
||||
#error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
|
||||
#endif
|
||||
#endif /* LWIP_TCP */
|
||||
#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */
|
||||
|
||||
/**
|
||||
* @ingroup lwip_nosys
|
||||
* Initialize all modules.
|
||||
* Use this in NO_SYS mode. Use tcpip_init() otherwise.
|
||||
*/
|
||||
void
|
||||
lwip_init(void)
|
||||
{
|
||||
#ifndef LWIP_SKIP_PACKING_CHECK
|
||||
LWIP_ASSERT("Struct packing not implemented correctly. Check your lwIP port.", sizeof(struct packed_struct_test) == PACKED_STRUCT_TEST_EXPECTED_SIZE);
|
||||
#endif
|
||||
|
||||
/* Modules initialization */
|
||||
stats_init();
|
||||
#if !NO_SYS
|
||||
sys_init();
|
||||
#endif /* !NO_SYS */
|
||||
mem_init();
|
||||
memp_init();
|
||||
pbuf_init();
|
||||
netif_init();
|
||||
#if LWIP_IPV4
|
||||
ip_init();
|
||||
#if LWIP_ARP
|
||||
etharp_init();
|
||||
#endif /* LWIP_ARP */
|
||||
#endif /* LWIP_IPV4 */
|
||||
#if LWIP_RAW
|
||||
raw_init();
|
||||
#endif /* LWIP_RAW */
|
||||
#if LWIP_UDP
|
||||
udp_init();
|
||||
#endif /* LWIP_UDP */
|
||||
#if LWIP_TCP
|
||||
tcp_init();
|
||||
#endif /* LWIP_TCP */
|
||||
#if LWIP_IGMP
|
||||
igmp_init();
|
||||
#endif /* LWIP_IGMP */
|
||||
#if LWIP_DNS
|
||||
dns_init();
|
||||
#endif /* LWIP_DNS */
|
||||
#if PPP_SUPPORT
|
||||
ppp_init();
|
||||
#endif
|
||||
|
||||
#if LWIP_TIMERS
|
||||
sys_timeouts_init();
|
||||
#endif /* LWIP_TIMERS */
|
||||
}
|
||||
124
src/core/ip.c
Normal file
124
src/core/ip.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* @file
|
||||
* Common IPv4 and IPv6 code
|
||||
*
|
||||
* @defgroup ip IP
|
||||
* @ingroup callbackstyle_api
|
||||
*
|
||||
* @defgroup ip4 IPv4
|
||||
* @ingroup ip
|
||||
*
|
||||
* @defgroup ip6 IPv6
|
||||
* @ingroup ip
|
||||
*
|
||||
* @defgroup ipaddr IP address handling
|
||||
* @ingroup infrastructure
|
||||
*
|
||||
* @defgroup ip4addr IPv4 only
|
||||
* @ingroup ipaddr
|
||||
*
|
||||
* @defgroup ip6addr IPv6 only
|
||||
* @ingroup ipaddr
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV4 || LWIP_IPV6
|
||||
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/ip.h"
|
||||
|
||||
/** Global data for both IPv4 and IPv6 */
|
||||
struct ip_globals ip_data;
|
||||
|
||||
#if LWIP_IPV4 && LWIP_IPV6
|
||||
|
||||
const ip_addr_t ip_addr_any_type = IPADDR_ANY_TYPE_INIT;
|
||||
|
||||
/**
|
||||
* @ingroup ipaddr
|
||||
* Convert IP address string (both versions) to numeric.
|
||||
* The version is auto-detected from the string.
|
||||
*
|
||||
* @param cp IP address string to convert
|
||||
* @param addr conversion result is stored here
|
||||
* @return 1 on success, 0 on error
|
||||
*/
|
||||
int
|
||||
ipaddr_aton(const char *cp, ip_addr_t *addr)
|
||||
{
|
||||
if (cp != NULL) {
|
||||
const char* c;
|
||||
for (c = cp; *c != 0; c++) {
|
||||
if (*c == ':') {
|
||||
/* contains a colon: IPv6 address */
|
||||
if (addr) {
|
||||
IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V6);
|
||||
}
|
||||
return ip6addr_aton(cp, ip_2_ip6(addr));
|
||||
} else if (*c == '.') {
|
||||
/* contains a dot: IPv4 address */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* call ip4addr_aton as fallback or if IPv4 was found */
|
||||
if (addr) {
|
||||
IP_SET_TYPE_VAL(*addr, IPADDR_TYPE_V4);
|
||||
}
|
||||
return ip4addr_aton(cp, ip_2_ip4(addr));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup lwip_nosys
|
||||
* If both IP versions are enabled, this function can dispatch packets to the correct one.
|
||||
* Don't call directly, pass to netif_add() and call netif->input().
|
||||
*/
|
||||
err_t
|
||||
ip_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
if (p != NULL) {
|
||||
if (IP_HDR_GET_VERSION(p->payload) == 6) {
|
||||
return ip6_input(p, inp);
|
||||
}
|
||||
return ip4_input(p, inp);
|
||||
}
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_IPV6 */
|
||||
|
||||
#endif /* LWIP_IPV4 || LWIP_IPV6 */
|
||||
529
src/core/ipv4/autoip.c
Normal file
529
src/core/ipv4/autoip.c
Normal file
@@ -0,0 +1,529 @@
|
||||
/**
|
||||
* @file
|
||||
* AutoIP Automatic LinkLocal IP Configuration
|
||||
*
|
||||
* This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
|
||||
* with RFC 3927.
|
||||
*
|
||||
* @defgroup autoip AUTOIP
|
||||
* @ingroup ip4
|
||||
* AUTOIP related functions
|
||||
* USAGE:
|
||||
*
|
||||
* define @ref LWIP_AUTOIP 1 in your lwipopts.h
|
||||
* Options:
|
||||
* AUTOIP_TMR_INTERVAL msecs,
|
||||
* I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
|
||||
* Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
|
||||
*
|
||||
* Without DHCP:
|
||||
* - Call autoip_start() after netif_add().
|
||||
*
|
||||
* With DHCP:
|
||||
* - define @ref LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h.
|
||||
* - Configure your DHCP Client.
|
||||
*
|
||||
* @see netifapi_autoip
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* Author: Dominik Spies <kontakt@dspies.de>
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV4 && LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/mem.h"
|
||||
/* #include "lwip/udp.h" */
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/autoip.h"
|
||||
#include "lwip/etharp.h"
|
||||
#include "lwip/prot/autoip.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/** Pseudo random macro based on netif informations.
|
||||
* You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
|
||||
#ifndef LWIP_AUTOIP_RAND
|
||||
#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
|
||||
((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
|
||||
((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
|
||||
((u32_t)((netif->hwaddr[4]) & 0xff))) + \
|
||||
(netif_autoip_data(netif)? netif_autoip_data(netif)->tried_llipaddr : 0))
|
||||
#endif /* LWIP_AUTOIP_RAND */
|
||||
|
||||
/**
|
||||
* Macro that generates the initial IP address to be tried by AUTOIP.
|
||||
* If you want to override this, define it to something else in lwipopts.h.
|
||||
*/
|
||||
#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR
|
||||
#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \
|
||||
lwip_htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
|
||||
((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)))
|
||||
#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */
|
||||
|
||||
/* static functions */
|
||||
static err_t autoip_arp_announce(struct netif *netif);
|
||||
static void autoip_start_probing(struct netif *netif);
|
||||
|
||||
#define netif_autoip_data(netif) ((struct autoip*)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP))
|
||||
|
||||
/**
|
||||
* @ingroup autoip
|
||||
* Set a statically allocated struct autoip to work with.
|
||||
* Using this prevents autoip_start to allocate it using mem_malloc.
|
||||
*
|
||||
* @param netif the netif for which to set the struct autoip
|
||||
* @param autoip (uninitialised) autoip struct allocated by the application
|
||||
*/
|
||||
void
|
||||
autoip_set_struct(struct netif *netif, struct autoip *autoip)
|
||||
{
|
||||
LWIP_ASSERT("netif != NULL", netif != NULL);
|
||||
LWIP_ASSERT("autoip != NULL", autoip != NULL);
|
||||
LWIP_ASSERT("netif already has a struct autoip set",
|
||||
netif_autoip_data(netif) == NULL);
|
||||
|
||||
/* clear data structure */
|
||||
memset(autoip, 0, sizeof(struct autoip));
|
||||
/* autoip->state = AUTOIP_STATE_OFF; */
|
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
|
||||
}
|
||||
|
||||
/** Restart AutoIP client and check the next address (conflict detected)
|
||||
*
|
||||
* @param netif The netif under AutoIP control
|
||||
*/
|
||||
static void
|
||||
autoip_restart(struct netif *netif)
|
||||
{
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
autoip->tried_llipaddr++;
|
||||
autoip_start(netif);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a IP address conflict after an ARP conflict detection
|
||||
*/
|
||||
static void
|
||||
autoip_handle_arp_conflict(struct netif *netif)
|
||||
{
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
|
||||
/* RFC3927, 2.5 "Conflict Detection and Defense" allows two options where
|
||||
a) means retreat on the first conflict and
|
||||
b) allows to keep an already configured address when having only one
|
||||
conflict in 10 seconds
|
||||
We use option b) since it helps to improve the chance that one of the two
|
||||
conflicting hosts may be able to retain its address. */
|
||||
|
||||
if (autoip->lastconflict > 0) {
|
||||
/* retreat, there was a conflicting ARP in the last DEFEND_INTERVAL seconds */
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
|
||||
|
||||
/* Active TCP sessions are aborted when removing the ip addresss */
|
||||
autoip_restart(netif);
|
||||
} else {
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
|
||||
autoip_arp_announce(netif);
|
||||
autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an IP-Address out of range 169.254.1.0 to 169.254.254.255
|
||||
*
|
||||
* @param netif network interface on which create the IP-Address
|
||||
* @param ipaddr ip address to initialize
|
||||
*/
|
||||
static void
|
||||
autoip_create_addr(struct netif *netif, ip4_addr_t *ipaddr)
|
||||
{
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
|
||||
/* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
|
||||
* compliant to RFC 3927 Section 2.1
|
||||
* We have 254 * 256 possibilities */
|
||||
|
||||
u32_t addr = lwip_ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
|
||||
addr += autoip->tried_llipaddr;
|
||||
addr = AUTOIP_NET | (addr & 0xffff);
|
||||
/* Now, 169.254.0.0 <= addr <= 169.254.255.255 */
|
||||
|
||||
if (addr < AUTOIP_RANGE_START) {
|
||||
addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
|
||||
}
|
||||
if (addr > AUTOIP_RANGE_END) {
|
||||
addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
|
||||
}
|
||||
LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) &&
|
||||
(addr <= AUTOIP_RANGE_END));
|
||||
ip4_addr_set_u32(ipaddr, lwip_htonl(addr));
|
||||
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
(u16_t)(autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr),
|
||||
ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an ARP probe from a network interface
|
||||
*
|
||||
* @param netif network interface used to send the probe
|
||||
*/
|
||||
static err_t
|
||||
autoip_arp_probe(struct netif *netif)
|
||||
{
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
/* this works because netif->ip_addr is ANY */
|
||||
return etharp_request(netif, &autoip->llipaddr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an ARP announce from a network interface
|
||||
*
|
||||
* @param netif network interface used to send the announce
|
||||
*/
|
||||
static err_t
|
||||
autoip_arp_announce(struct netif *netif)
|
||||
{
|
||||
return etharp_gratuitous(netif);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure interface for use with current LL IP-Address
|
||||
*
|
||||
* @param netif network interface to configure with current LL IP-Address
|
||||
*/
|
||||
static err_t
|
||||
autoip_bind(struct netif *netif)
|
||||
{
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
ip4_addr_t sn_mask, gw_addr;
|
||||
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
|
||||
("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
(void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
|
||||
ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
|
||||
ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
|
||||
|
||||
IP4_ADDR(&sn_mask, 255, 255, 0, 0);
|
||||
IP4_ADDR(&gw_addr, 0, 0, 0, 0);
|
||||
|
||||
netif_set_addr(netif, &autoip->llipaddr, &sn_mask, &gw_addr);
|
||||
/* interface is used by routing now that an address is set */
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup autoip
|
||||
* Start AutoIP client
|
||||
*
|
||||
* @param netif network interface on which start the AutoIP client
|
||||
*/
|
||||
err_t
|
||||
autoip_start(struct netif *netif)
|
||||
{
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
err_t result = ERR_OK;
|
||||
|
||||
LWIP_ERROR("netif is not up, old style port?", netif_is_up(netif), return ERR_ARG;);
|
||||
|
||||
/* Set IP-Address, Netmask and Gateway to 0 to make sure that
|
||||
* ARP Packets are formed correctly
|
||||
*/
|
||||
netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
|
||||
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0],
|
||||
netif->name[1], (u16_t)netif->num));
|
||||
if (autoip == NULL) {
|
||||
/* no AutoIP client attached yet? */
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
|
||||
("autoip_start(): starting new AUTOIP client\n"));
|
||||
autoip = (struct autoip *)mem_malloc(sizeof(struct autoip));
|
||||
if (autoip == NULL) {
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
|
||||
("autoip_start(): could not allocate autoip\n"));
|
||||
return ERR_MEM;
|
||||
}
|
||||
memset(autoip, 0, sizeof(struct autoip));
|
||||
/* store this AutoIP client in the netif */
|
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
|
||||
} else {
|
||||
autoip->state = AUTOIP_STATE_OFF;
|
||||
autoip->ttw = 0;
|
||||
autoip->sent_num = 0;
|
||||
ip4_addr_set_zero(&autoip->llipaddr);
|
||||
autoip->lastconflict = 0;
|
||||
}
|
||||
|
||||
autoip_create_addr(netif, &(autoip->llipaddr));
|
||||
autoip_start_probing(netif);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
autoip_start_probing(struct netif *netif)
|
||||
{
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
|
||||
autoip->state = AUTOIP_STATE_PROBING;
|
||||
autoip->sent_num = 0;
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
|
||||
ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
|
||||
|
||||
/* time to wait to first probe, this is randomly
|
||||
* chosen out of 0 to PROBE_WAIT seconds.
|
||||
* compliant to RFC 3927 Section 2.2.1
|
||||
*/
|
||||
autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND));
|
||||
|
||||
/*
|
||||
* if we tried more then MAX_CONFLICTS we must limit our rate for
|
||||
* acquiring and probing address
|
||||
* compliant to RFC 3927 Section 2.2.1
|
||||
*/
|
||||
if (autoip->tried_llipaddr > MAX_CONFLICTS) {
|
||||
autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a possible change in the network configuration.
|
||||
*
|
||||
* If there is an AutoIP address configured, take the interface down
|
||||
* and begin probing with the same address.
|
||||
*/
|
||||
void
|
||||
autoip_network_changed(struct netif *netif)
|
||||
{
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
|
||||
if (autoip && (autoip->state != AUTOIP_STATE_OFF)) {
|
||||
autoip_start_probing(netif);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup autoip
|
||||
* Stop AutoIP client
|
||||
*
|
||||
* @param netif network interface on which stop the AutoIP client
|
||||
*/
|
||||
err_t
|
||||
autoip_stop(struct netif *netif)
|
||||
{
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
|
||||
if (autoip != NULL) {
|
||||
autoip->state = AUTOIP_STATE_OFF;
|
||||
if (ip4_addr_islinklocal(netif_ip4_addr(netif))) {
|
||||
netif_set_addr(netif, IP4_ADDR_ANY4, IP4_ADDR_ANY4, IP4_ADDR_ANY4);
|
||||
}
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
|
||||
*/
|
||||
void
|
||||
autoip_tmr(void)
|
||||
{
|
||||
struct netif *netif = netif_list;
|
||||
/* loop through netif's */
|
||||
while (netif != NULL) {
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
/* only act on AutoIP configured interfaces */
|
||||
if (autoip != NULL) {
|
||||
if (autoip->lastconflict > 0) {
|
||||
autoip->lastconflict--;
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
|
||||
("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
|
||||
(u16_t)(autoip->state), autoip->ttw));
|
||||
|
||||
if (autoip->ttw > 0) {
|
||||
autoip->ttw--;
|
||||
}
|
||||
|
||||
switch(autoip->state) {
|
||||
case AUTOIP_STATE_PROBING:
|
||||
if (autoip->ttw == 0) {
|
||||
if (autoip->sent_num >= PROBE_NUM) {
|
||||
/* Switch to ANNOUNCING: now we can bind to an IP address and use it */
|
||||
autoip->state = AUTOIP_STATE_ANNOUNCING;
|
||||
autoip_bind(netif);
|
||||
/* autoip_bind() calls netif_set_addr(): this triggers a gratuitous ARP
|
||||
which counts as an announcement */
|
||||
autoip->sent_num = 1;
|
||||
autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
|
||||
ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
|
||||
} else {
|
||||
autoip_arp_probe(netif);
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() PROBING Sent Probe\n"));
|
||||
autoip->sent_num++;
|
||||
if (autoip->sent_num == PROBE_NUM) {
|
||||
/* calculate time to wait to for announce */
|
||||
autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
|
||||
} else {
|
||||
/* calculate time to wait to next probe */
|
||||
autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
|
||||
((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
|
||||
PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case AUTOIP_STATE_ANNOUNCING:
|
||||
if (autoip->ttw == 0) {
|
||||
autoip_arp_announce(netif);
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_tmr() ANNOUNCING Sent Announce\n"));
|
||||
autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
|
||||
autoip->sent_num++;
|
||||
|
||||
if (autoip->sent_num >= ANNOUNCE_NUM) {
|
||||
autoip->state = AUTOIP_STATE_BOUND;
|
||||
autoip->sent_num = 0;
|
||||
autoip->ttw = 0;
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
|
||||
("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
|
||||
ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
|
||||
ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* nothing to do in other states */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* proceed to next network interface */
|
||||
netif = netif->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles every incoming ARP Packet, called by etharp_input().
|
||||
*
|
||||
* @param netif network interface to use for autoip processing
|
||||
* @param hdr Incoming ARP packet
|
||||
*/
|
||||
void
|
||||
autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
|
||||
{
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n"));
|
||||
if ((autoip != NULL) && (autoip->state != AUTOIP_STATE_OFF)) {
|
||||
/* when ip.src == llipaddr && hw.src != netif->hwaddr
|
||||
*
|
||||
* when probing ip.dst == llipaddr && hw.src != netif->hwaddr
|
||||
* we have a conflict and must solve it
|
||||
*/
|
||||
ip4_addr_t sipaddr, dipaddr;
|
||||
struct eth_addr netifaddr;
|
||||
ETHADDR16_COPY(netifaddr.addr, netif->hwaddr);
|
||||
|
||||
/* Copy struct ip4_addr2 to aligned ip4_addr, to support compilers without
|
||||
* structure packing (not using structure copy which breaks strict-aliasing rules).
|
||||
*/
|
||||
IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
|
||||
IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
|
||||
|
||||
if (autoip->state == AUTOIP_STATE_PROBING) {
|
||||
/* RFC 3927 Section 2.2.1:
|
||||
* from beginning to after ANNOUNCE_WAIT
|
||||
* seconds we have a conflict if
|
||||
* ip.src == llipaddr OR
|
||||
* ip.dst == llipaddr && hw.src != own hwaddr
|
||||
*/
|
||||
if ((ip4_addr_cmp(&sipaddr, &autoip->llipaddr)) ||
|
||||
(ip4_addr_cmp(&dipaddr, &autoip->llipaddr) &&
|
||||
!eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
|
||||
("autoip_arp_reply(): Probe Conflict detected\n"));
|
||||
autoip_restart(netif);
|
||||
}
|
||||
} else {
|
||||
/* RFC 3927 Section 2.5:
|
||||
* in any state we have a conflict if
|
||||
* ip.src == llipaddr && hw.src != own hwaddr
|
||||
*/
|
||||
if (ip4_addr_cmp(&sipaddr, &autoip->llipaddr) &&
|
||||
!eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
|
||||
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
|
||||
("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
|
||||
autoip_handle_arp_conflict(netif);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** check if AutoIP supplied netif->ip_addr
|
||||
*
|
||||
* @param netif the netif to check
|
||||
* @return 1 if AutoIP supplied netif->ip_addr (state BOUND or ANNOUNCING),
|
||||
* 0 otherwise
|
||||
*/
|
||||
u8_t
|
||||
autoip_supplied_address(const struct netif *netif)
|
||||
{
|
||||
if ((netif != NULL) && (netif_autoip_data(netif) != NULL)) {
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
return (autoip->state == AUTOIP_STATE_BOUND) || (autoip->state == AUTOIP_STATE_ANNOUNCING);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8_t
|
||||
autoip_accept_packet(struct netif *netif, const ip4_addr_t *addr)
|
||||
{
|
||||
struct autoip* autoip = netif_autoip_data(netif);
|
||||
return (autoip != NULL) && ip4_addr_cmp(addr, &(autoip->llipaddr));
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_AUTOIP */
|
||||
1932
src/core/ipv4/dhcp.c
Normal file
1932
src/core/ipv4/dhcp.c
Normal file
File diff suppressed because it is too large
Load Diff
1224
src/core/ipv4/etharp.c
Normal file
1224
src/core/ipv4/etharp.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,11 @@
|
||||
/**
|
||||
* @file
|
||||
* ICMP - Internet Control Message Protocol
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
@@ -35,190 +41,353 @@
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV4 && LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/icmp.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/def.h"
|
||||
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
#include <string.h>
|
||||
|
||||
/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
|
||||
* used to modify and send a response packet (and to 1 if this is not the case,
|
||||
* e.g. when link header is stripped of when receiving) */
|
||||
#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
|
||||
#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
|
||||
#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
|
||||
|
||||
/* The amount of data from the original packet to return in a dest-unreachable */
|
||||
#define ICMP_DEST_UNREACH_DATASIZE 8
|
||||
|
||||
static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
|
||||
|
||||
/**
|
||||
* Processes ICMP input packets, called from ip_input().
|
||||
*
|
||||
* Currently only processes icmp echo requests and sends
|
||||
* out the echo response.
|
||||
*
|
||||
* @param p the icmp echo request packet, p->payload pointing to the icmp header
|
||||
* @param inp the netif on which this packet was received
|
||||
*/
|
||||
void
|
||||
icmp_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
unsigned char type;
|
||||
unsigned char code;
|
||||
u8_t type;
|
||||
#ifdef LWIP_DEBUG
|
||||
u8_t code;
|
||||
#endif /* LWIP_DEBUG */
|
||||
struct icmp_echo_hdr *iecho;
|
||||
struct ip_hdr *iphdr;
|
||||
struct ip_addr tmpaddr;
|
||||
u16_t hlen;
|
||||
const struct ip_hdr *iphdr_in;
|
||||
s16_t hlen;
|
||||
const ip4_addr_t* src;
|
||||
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.recv;
|
||||
#endif /* ICMP_STATS */
|
||||
snmp_inc_icmpinmsgs();
|
||||
ICMP_STATS_INC(icmp.recv);
|
||||
MIB2_STATS_INC(mib2.icmpinmsgs);
|
||||
|
||||
|
||||
iphdr = p->payload;
|
||||
hlen = IPH_HL(iphdr) * 4;
|
||||
if (pbuf_header(p, -((s16_t)hlen)) || (p->tot_len < sizeof(u16_t)*2)) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%u bytes) received\n", p->tot_len));
|
||||
pbuf_free(p);
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.lenerr;
|
||||
#endif /* ICMP_STATS */
|
||||
snmp_inc_icmpinerrors();
|
||||
return;
|
||||
iphdr_in = ip4_current_header();
|
||||
hlen = IPH_HL(iphdr_in) * 4;
|
||||
if (hlen < IP_HLEN) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short IP header (%"S16_F" bytes) received\n", hlen));
|
||||
goto lenerr;
|
||||
}
|
||||
if (p->len < sizeof(u16_t)*2) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
|
||||
goto lenerr;
|
||||
}
|
||||
|
||||
type = *((u8_t *)p->payload);
|
||||
#ifdef LWIP_DEBUG
|
||||
code = *(((u8_t *)p->payload)+1);
|
||||
#endif /* LWIP_DEBUG */
|
||||
switch (type) {
|
||||
case ICMP_ER:
|
||||
/* This is OK, echo reply might have been parsed by a raw PCB
|
||||
(as obviously, an echo request has been sent, too). */
|
||||
MIB2_STATS_INC(mib2.icmpinechoreps);
|
||||
break;
|
||||
case ICMP_ECHO:
|
||||
if (ip_addr_isbroadcast(&iphdr->dest, &inp->netmask) ||
|
||||
ip_addr_ismulticast(&iphdr->dest)) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("Smurf.\n"));
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.err;
|
||||
#endif /* ICMP_STATS */
|
||||
pbuf_free(p);
|
||||
return;
|
||||
MIB2_STATS_INC(mib2.icmpinechos);
|
||||
src = ip4_current_dest_addr();
|
||||
/* multicast destination address? */
|
||||
if (ip4_addr_ismulticast(ip4_current_dest_addr())) {
|
||||
#if LWIP_MULTICAST_PING
|
||||
/* For multicast, use address of receiving interface as source address */
|
||||
src = netif_ip4_addr(inp);
|
||||
#else /* LWIP_MULTICAST_PING */
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast pings\n"));
|
||||
goto icmperr;
|
||||
#endif /* LWIP_MULTICAST_PING */
|
||||
}
|
||||
/* broadcast destination address? */
|
||||
if (ip4_addr_isbroadcast(ip4_current_dest_addr(), ip_current_netif())) {
|
||||
#if LWIP_BROADCAST_PING
|
||||
/* For broadcast, use address of receiving interface as source address */
|
||||
src = netif_ip4_addr(inp);
|
||||
#else /* LWIP_BROADCAST_PING */
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to broadcast pings\n"));
|
||||
goto icmperr;
|
||||
#endif /* LWIP_BROADCAST_PING */
|
||||
}
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
|
||||
LWIP_DEBUGF(DEMO_DEBUG, ("Pong!\n"));
|
||||
if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
|
||||
pbuf_free(p);
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.lenerr;
|
||||
#endif /* ICMP_STATS */
|
||||
snmp_inc_icmpinerrors();
|
||||
|
||||
return;
|
||||
goto lenerr;
|
||||
}
|
||||
iecho = p->payload;
|
||||
if (inet_chksum_pbuf(p) != 0) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
|
||||
pbuf_free(p);
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.chkerr;
|
||||
#endif /* ICMP_STATS */
|
||||
snmp_inc_icmpinerrors();
|
||||
return;
|
||||
#if CHECKSUM_CHECK_ICMP
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP) {
|
||||
if (inet_chksum_pbuf(p) != 0) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
|
||||
pbuf_free(p);
|
||||
ICMP_STATS_INC(icmp.chkerr);
|
||||
MIB2_STATS_INC(mib2.icmpinerrors);
|
||||
return;
|
||||
}
|
||||
}
|
||||
tmpaddr.addr = iphdr->src.addr;
|
||||
iphdr->src.addr = iphdr->dest.addr;
|
||||
iphdr->dest.addr = tmpaddr.addr;
|
||||
ICMPH_TYPE_SET(iecho, ICMP_ER);
|
||||
/* adjust the checksum */
|
||||
if (iecho->chksum >= htons(0xffff - (ICMP_ECHO << 8))) {
|
||||
iecho->chksum += htons(ICMP_ECHO << 8) + 1;
|
||||
#endif
|
||||
#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
|
||||
if (pbuf_header(p, (hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
|
||||
/* p is not big enough to contain link headers
|
||||
* allocate a new one and copy p into it
|
||||
*/
|
||||
struct pbuf *r;
|
||||
/* allocate new packet buffer with space for link headers */
|
||||
r = pbuf_alloc(PBUF_LINK, p->tot_len + hlen, PBUF_RAM);
|
||||
if (r == NULL) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
|
||||
goto icmperr;
|
||||
}
|
||||
if (r->len < hlen + sizeof(struct icmp_echo_hdr)) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("first pbuf cannot hold the ICMP header"));
|
||||
pbuf_free(r);
|
||||
goto icmperr;
|
||||
}
|
||||
/* copy the ip header */
|
||||
MEMCPY(r->payload, iphdr_in, hlen);
|
||||
/* switch r->payload back to icmp header (cannot fail) */
|
||||
if (pbuf_header(r, -hlen)) {
|
||||
LWIP_ASSERT("icmp_input: moving r->payload to icmp header failed\n", 0);
|
||||
pbuf_free(r);
|
||||
goto icmperr;
|
||||
}
|
||||
/* copy the rest of the packet without ip header */
|
||||
if (pbuf_copy(r, p) != ERR_OK) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("icmp_input: copying to new pbuf failed"));
|
||||
pbuf_free(r);
|
||||
goto icmperr;
|
||||
}
|
||||
/* free the original p */
|
||||
pbuf_free(p);
|
||||
/* we now have an identical copy of p that has room for link headers */
|
||||
p = r;
|
||||
} else {
|
||||
iecho->chksum += htons(ICMP_ECHO << 8);
|
||||
/* restore p->payload to point to icmp header (cannot fail) */
|
||||
if (pbuf_header(p, -(s16_t)(hlen + PBUF_LINK_HLEN + PBUF_LINK_ENCAPSULATION_HLEN))) {
|
||||
LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
|
||||
goto icmperr;
|
||||
}
|
||||
}
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.xmit;
|
||||
#endif /* ICMP_STATS */
|
||||
/* increase number of messages attempted to send */
|
||||
snmp_inc_icmpoutmsgs();
|
||||
/* increase number of echo replies attempted to send */
|
||||
snmp_inc_icmpoutechoreps();
|
||||
#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
|
||||
/* At this point, all checks are OK. */
|
||||
/* We generate an answer by switching the dest and src ip addresses,
|
||||
* setting the icmp type to ECHO_RESPONSE and updating the checksum. */
|
||||
iecho = (struct icmp_echo_hdr *)p->payload;
|
||||
if (pbuf_header(p, hlen)) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Can't move over header in packet"));
|
||||
} else {
|
||||
err_t ret;
|
||||
struct ip_hdr *iphdr = (struct ip_hdr*)p->payload;
|
||||
ip4_addr_copy(iphdr->src, *src);
|
||||
ip4_addr_copy(iphdr->dest, *ip4_current_src_addr());
|
||||
ICMPH_TYPE_SET(iecho, ICMP_ER);
|
||||
#if CHECKSUM_GEN_ICMP
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) {
|
||||
/* adjust the checksum */
|
||||
if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
|
||||
iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1;
|
||||
} else {
|
||||
iecho->chksum += PP_HTONS(ICMP_ECHO << 8);
|
||||
}
|
||||
}
|
||||
#if LWIP_CHECKSUM_CTRL_PER_NETIF
|
||||
else {
|
||||
iecho->chksum = 0;
|
||||
}
|
||||
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF */
|
||||
#else /* CHECKSUM_GEN_ICMP */
|
||||
iecho->chksum = 0;
|
||||
#endif /* CHECKSUM_GEN_ICMP */
|
||||
|
||||
pbuf_header(p, hlen);
|
||||
ip_output_if (p, &(iphdr->src), IP_HDRINCL,
|
||||
IPH_TTL(iphdr), IP_PROTO_ICMP, inp);
|
||||
/* Set the correct TTL and recalculate the header checksum. */
|
||||
IPH_TTL_SET(iphdr, ICMP_TTL);
|
||||
IPH_CHKSUM_SET(iphdr, 0);
|
||||
#if CHECKSUM_GEN_IP
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_IP) {
|
||||
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, hlen));
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_IP */
|
||||
|
||||
ICMP_STATS_INC(icmp.xmit);
|
||||
/* increase number of messages attempted to send */
|
||||
MIB2_STATS_INC(mib2.icmpoutmsgs);
|
||||
/* increase number of echo replies attempted to send */
|
||||
MIB2_STATS_INC(mib2.icmpoutechoreps);
|
||||
|
||||
/* send an ICMP packet */
|
||||
ret = ip4_output_if(p, src, LWIP_IP_HDRINCL,
|
||||
ICMP_TTL, 0, IP_PROTO_ICMP, inp);
|
||||
if (ret != ERR_OK) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %s\n", lwip_strerr(ret)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %d code %d not supported.\n", (int)type, (int)code));
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.proterr;
|
||||
++lwip_stats.icmp.drop;
|
||||
#endif /* ICMP_STATS */
|
||||
if (type == ICMP_DUR) {
|
||||
MIB2_STATS_INC(mib2.icmpindestunreachs);
|
||||
} else if (type == ICMP_TE) {
|
||||
MIB2_STATS_INC(mib2.icmpindestunreachs);
|
||||
} else if (type == ICMP_PP) {
|
||||
MIB2_STATS_INC(mib2.icmpinparmprobs);
|
||||
} else if (type == ICMP_SQ) {
|
||||
MIB2_STATS_INC(mib2.icmpinsrcquenchs);
|
||||
} else if (type == ICMP_RD) {
|
||||
MIB2_STATS_INC(mib2.icmpinredirects);
|
||||
} else if (type == ICMP_TS) {
|
||||
MIB2_STATS_INC(mib2.icmpintimestamps);
|
||||
} else if (type == ICMP_TSR) {
|
||||
MIB2_STATS_INC(mib2.icmpintimestampreps);
|
||||
} else if (type == ICMP_AM) {
|
||||
MIB2_STATS_INC(mib2.icmpinaddrmasks);
|
||||
} else if (type == ICMP_AMR) {
|
||||
MIB2_STATS_INC(mib2.icmpinaddrmaskreps);
|
||||
}
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",
|
||||
(s16_t)type, (s16_t)code));
|
||||
ICMP_STATS_INC(icmp.proterr);
|
||||
ICMP_STATS_INC(icmp.drop);
|
||||
}
|
||||
pbuf_free(p);
|
||||
return;
|
||||
lenerr:
|
||||
pbuf_free(p);
|
||||
ICMP_STATS_INC(icmp.lenerr);
|
||||
MIB2_STATS_INC(mib2.icmpinerrors);
|
||||
return;
|
||||
#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
|
||||
icmperr:
|
||||
pbuf_free(p);
|
||||
ICMP_STATS_INC(icmp.err);
|
||||
MIB2_STATS_INC(mib2.icmpinerrors);
|
||||
return;
|
||||
#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Send an icmp 'destination unreachable' packet, called from ip_input() if
|
||||
* the transport layer protocol is unknown and from udp_input() if the local
|
||||
* port is not bound.
|
||||
*
|
||||
* @param p the input packet for which the 'unreachable' should be sent,
|
||||
* p->payload pointing to the IP header
|
||||
* @param t type of the 'unreachable' packet
|
||||
*/
|
||||
void
|
||||
icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
|
||||
{
|
||||
struct pbuf *q;
|
||||
struct ip_hdr *iphdr;
|
||||
struct icmp_dur_hdr *idur;
|
||||
|
||||
q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
|
||||
/* ICMP header + IP header + 8 bytes of data */
|
||||
|
||||
iphdr = p->payload;
|
||||
|
||||
idur = q->payload;
|
||||
ICMPH_TYPE_SET(idur, ICMP_DUR);
|
||||
ICMPH_CODE_SET(idur, t);
|
||||
|
||||
memcpy((char *)q->payload + 8, p->payload, IP_HLEN + 8);
|
||||
|
||||
/* calculate checksum */
|
||||
idur->chksum = 0;
|
||||
idur->chksum = inet_chksum(idur, q->len);
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.xmit;
|
||||
#endif /* ICMP_STATS */
|
||||
/* increase number of messages attempted to send */
|
||||
snmp_inc_icmpoutmsgs();
|
||||
/* increase number of destination unreachable messages attempted to send */
|
||||
snmp_inc_icmpoutdestunreachs();
|
||||
|
||||
ip_output(q, NULL, &(iphdr->src),
|
||||
ICMP_TTL, IP_PROTO_ICMP);
|
||||
pbuf_free(q);
|
||||
MIB2_STATS_INC(mib2.icmpoutdestunreachs);
|
||||
icmp_send_response(p, ICMP_DUR, t);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
#if IP_FORWARD
|
||||
|
||||
#if IP_FORWARD || IP_REASSEMBLY
|
||||
/**
|
||||
* Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
|
||||
*
|
||||
* @param p the input packet for which the 'time exceeded' should be sent,
|
||||
* p->payload pointing to the IP header
|
||||
* @param t type of the 'time exceeded' packet
|
||||
*/
|
||||
void
|
||||
icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
|
||||
{
|
||||
MIB2_STATS_INC(mib2.icmpouttimeexcds);
|
||||
icmp_send_response(p, ICMP_TE, t);
|
||||
}
|
||||
|
||||
#endif /* IP_FORWARD || IP_REASSEMBLY */
|
||||
|
||||
/**
|
||||
* Send an icmp packet in response to an incoming packet.
|
||||
*
|
||||
* @param p the input packet for which the 'unreachable' should be sent,
|
||||
* p->payload pointing to the IP header
|
||||
* @param type Type of the ICMP header
|
||||
* @param code Code of the ICMP header
|
||||
*/
|
||||
static void
|
||||
icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
|
||||
{
|
||||
struct pbuf *q;
|
||||
struct ip_hdr *iphdr;
|
||||
struct icmp_te_hdr *tehdr;
|
||||
/* we can use the echo header here */
|
||||
struct icmp_echo_hdr *icmphdr;
|
||||
ip4_addr_t iphdr_src;
|
||||
struct netif *netif;
|
||||
|
||||
q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
|
||||
/* increase number of messages attempted to send */
|
||||
MIB2_STATS_INC(mib2.icmpoutmsgs);
|
||||
|
||||
iphdr = p->payload;
|
||||
#if ICMP_DEBUG
|
||||
/* ICMP header + IP header + 8 bytes of data */
|
||||
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
|
||||
PBUF_RAM);
|
||||
if (q == NULL) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
|
||||
MIB2_STATS_INC(mib2.icmpouterrors);
|
||||
return;
|
||||
}
|
||||
LWIP_ASSERT("check that first pbuf can hold icmp message",
|
||||
(q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
|
||||
|
||||
iphdr = (struct ip_hdr *)p->payload;
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
|
||||
ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));
|
||||
ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src);
|
||||
LWIP_DEBUGF(ICMP_DEBUG, (" to "));
|
||||
ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));
|
||||
ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest);
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
|
||||
#endif /* ICMP_DEBNUG */
|
||||
|
||||
tehdr = q->payload;
|
||||
ICMPH_TYPE_SET(tehdr, ICMP_TE);
|
||||
ICMPH_CODE_SET(tehdr, t);
|
||||
icmphdr = (struct icmp_echo_hdr *)q->payload;
|
||||
icmphdr->type = type;
|
||||
icmphdr->code = code;
|
||||
icmphdr->id = 0;
|
||||
icmphdr->seqno = 0;
|
||||
|
||||
/* copy fields from original packet */
|
||||
memcpy((char *)q->payload + 8, (char *)p->payload, IP_HLEN + 8);
|
||||
SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
|
||||
IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
|
||||
|
||||
/* calculate checksum */
|
||||
tehdr->chksum = 0;
|
||||
tehdr->chksum = inet_chksum(tehdr, q->len);
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.xmit;
|
||||
#endif /* ICMP_STATS */
|
||||
/* increase number of messages attempted to send */
|
||||
snmp_inc_icmpoutmsgs();
|
||||
/* increase number of destination unreachable messages attempted to send */
|
||||
snmp_inc_icmpouttimeexcds();
|
||||
ip_output(q, NULL, &(iphdr->src),
|
||||
ICMP_TTL, IP_PROTO_ICMP);
|
||||
ip4_addr_copy(iphdr_src, iphdr->src);
|
||||
#ifdef LWIP_HOOK_IP4_ROUTE_SRC
|
||||
{
|
||||
ip4_addr_t iphdr_dst;
|
||||
ip4_addr_copy(iphdr_dst, iphdr->dest);
|
||||
netif = ip4_route_src(&iphdr_src, &iphdr_dst);
|
||||
}
|
||||
#else
|
||||
netif = ip4_route(&iphdr_src);
|
||||
#endif
|
||||
if (netif != NULL) {
|
||||
/* calculate checksum */
|
||||
icmphdr->chksum = 0;
|
||||
#if CHECKSUM_GEN_ICMP
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP) {
|
||||
icmphdr->chksum = inet_chksum(icmphdr, q->len);
|
||||
}
|
||||
#endif
|
||||
ICMP_STATS_INC(icmp.xmit);
|
||||
ip4_output_if(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP, netif);
|
||||
}
|
||||
pbuf_free(q);
|
||||
}
|
||||
|
||||
#endif /* IP_FORWARDING > 0 */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_ICMP */
|
||||
|
||||
792
src/core/ipv4/igmp.c
Normal file
792
src/core/ipv4/igmp.c
Normal file
@@ -0,0 +1,792 @@
|
||||
/**
|
||||
* @file
|
||||
* IGMP - Internet Group Management Protocol
|
||||
*
|
||||
* @defgroup igmp IGMP
|
||||
* @ingroup ip4
|
||||
* To be called from TCPIP thread
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002 CITEL Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* This file is a contribution to the lwIP TCP/IP stack.
|
||||
* The Swedish Institute of Computer Science and Adam Dunkels
|
||||
* are specifically granted permission to redistribute this
|
||||
* source code.
|
||||
*/
|
||||
|
||||
/*-------------------------------------------------------------
|
||||
Note 1)
|
||||
Although the rfc requires V1 AND V2 capability
|
||||
we will only support v2 since now V1 is very old (August 1989)
|
||||
V1 can be added if required
|
||||
|
||||
a debug print and statistic have been implemented to
|
||||
show this up.
|
||||
-------------------------------------------------------------
|
||||
-------------------------------------------------------------
|
||||
Note 2)
|
||||
A query for a specific group address (as opposed to ALLHOSTS)
|
||||
has now been implemented as I am unsure if it is required
|
||||
|
||||
a debug print and statistic have been implemented to
|
||||
show this up.
|
||||
-------------------------------------------------------------
|
||||
-------------------------------------------------------------
|
||||
Note 3)
|
||||
The router alert rfc 2113 is implemented in outgoing packets
|
||||
but not checked rigorously incoming
|
||||
-------------------------------------------------------------
|
||||
Steve Reynolds
|
||||
------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* RFC 988 - Host extensions for IP multicasting - V0
|
||||
* RFC 1054 - Host extensions for IP multicasting -
|
||||
* RFC 1112 - Host extensions for IP multicasting - V1
|
||||
* RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard)
|
||||
* RFC 3376 - Internet Group Management Protocol, Version 3 - V3
|
||||
* RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+
|
||||
* RFC 2113 - IP Router Alert Option -
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* Includes
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV4 && LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/igmp.h"
|
||||
#include "lwip/debug.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/prot/igmp.h"
|
||||
|
||||
#include "string.h"
|
||||
|
||||
static struct igmp_group *igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr);
|
||||
static err_t igmp_remove_group(struct netif* netif, struct igmp_group *group);
|
||||
static void igmp_timeout(struct netif *netif, struct igmp_group *group);
|
||||
static void igmp_start_timer(struct igmp_group *group, u8_t max_time);
|
||||
static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
|
||||
static err_t igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif);
|
||||
static void igmp_send(struct netif *netif, struct igmp_group *group, u8_t type);
|
||||
|
||||
static ip4_addr_t allsystems;
|
||||
static ip4_addr_t allrouters;
|
||||
|
||||
/**
|
||||
* Initialize the IGMP module
|
||||
*/
|
||||
void
|
||||
igmp_init(void)
|
||||
{
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
|
||||
|
||||
IP4_ADDR(&allsystems, 224, 0, 0, 1);
|
||||
IP4_ADDR(&allrouters, 224, 0, 0, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start IGMP processing on interface
|
||||
*
|
||||
* @param netif network interface on which start IGMP processing
|
||||
*/
|
||||
err_t
|
||||
igmp_start(struct netif *netif)
|
||||
{
|
||||
struct igmp_group* group;
|
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void*)netif));
|
||||
|
||||
group = igmp_lookup_group(netif, &allsystems);
|
||||
|
||||
if (group != NULL) {
|
||||
group->group_state = IGMP_GROUP_IDLE_MEMBER;
|
||||
group->use++;
|
||||
|
||||
/* Allow the igmp messages at the MAC level */
|
||||
if (netif->igmp_mac_filter != NULL) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
|
||||
ip4_addr_debug_print_val(IGMP_DEBUG, allsystems);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
|
||||
netif->igmp_mac_filter(netif, &allsystems, NETIF_ADD_MAC_FILTER);
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop IGMP processing on interface
|
||||
*
|
||||
* @param netif network interface on which stop IGMP processing
|
||||
*/
|
||||
err_t
|
||||
igmp_stop(struct netif *netif)
|
||||
{
|
||||
struct igmp_group *group = netif_igmp_data(netif);
|
||||
|
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, NULL);
|
||||
|
||||
while (group != NULL) {
|
||||
struct igmp_group *next = group->next; /* avoid use-after-free below */
|
||||
|
||||
/* disable the group at the MAC level */
|
||||
if (netif->igmp_mac_filter != NULL) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, &group->group_address);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
|
||||
netif->igmp_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
|
||||
}
|
||||
|
||||
/* free group */
|
||||
memp_free(MEMP_IGMP_GROUP, group);
|
||||
|
||||
/* move to "next" */
|
||||
group = next;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report IGMP memberships for this interface
|
||||
*
|
||||
* @param netif network interface on which report IGMP memberships
|
||||
*/
|
||||
void
|
||||
igmp_report_groups(struct netif *netif)
|
||||
{
|
||||
struct igmp_group *group = netif_igmp_data(netif);
|
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", (void*)netif));
|
||||
|
||||
/* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
|
||||
if(group != NULL) {
|
||||
group = group->next;
|
||||
}
|
||||
|
||||
while (group != NULL) {
|
||||
igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
|
||||
group = group->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a group in the global igmp_group_list
|
||||
*
|
||||
* @param ifp the network interface for which to look
|
||||
* @param addr the group ip address to search for
|
||||
* @return a struct igmp_group* if the group has been found,
|
||||
* NULL if the group wasn't found.
|
||||
*/
|
||||
struct igmp_group *
|
||||
igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr)
|
||||
{
|
||||
struct igmp_group *group = netif_igmp_data(ifp);
|
||||
|
||||
while (group != NULL) {
|
||||
if (ip4_addr_cmp(&(group->group_address), addr)) {
|
||||
return group;
|
||||
}
|
||||
group = group->next;
|
||||
}
|
||||
|
||||
/* to be clearer, we return NULL here instead of
|
||||
* 'group' (which is also NULL at this point).
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a specific igmp group and create a new one if not found-
|
||||
*
|
||||
* @param ifp the network interface for which to look
|
||||
* @param addr the group ip address to search
|
||||
* @return a struct igmp_group*,
|
||||
* NULL on memory error.
|
||||
*/
|
||||
struct igmp_group *
|
||||
igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
|
||||
{
|
||||
struct igmp_group *group;
|
||||
|
||||
/* Search if the group already exists */
|
||||
group = igmp_lookfor_group(ifp, addr);
|
||||
if (group != NULL) {
|
||||
/* Group already exists. */
|
||||
return group;
|
||||
}
|
||||
|
||||
/* Group doesn't exist yet, create a new one */
|
||||
group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
|
||||
if (group != NULL) {
|
||||
ip4_addr_set(&(group->group_address), addr);
|
||||
group->timer = 0; /* Not running */
|
||||
group->group_state = IGMP_GROUP_NON_MEMBER;
|
||||
group->last_reporter_flag = 0;
|
||||
group->use = 0;
|
||||
group->next = netif_igmp_data(ifp);
|
||||
|
||||
netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group);
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, addr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)ifp));
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a group in the global igmp_group_list, but don't free it yet
|
||||
*
|
||||
* @param group the group to remove from the global igmp_group_list
|
||||
* @return ERR_OK if group was removed from the list, an err_t otherwise
|
||||
*/
|
||||
static err_t
|
||||
igmp_remove_group(struct netif* netif, struct igmp_group *group)
|
||||
{
|
||||
err_t err = ERR_OK;
|
||||
|
||||
/* Is it the first group? */
|
||||
if (netif_igmp_data(netif) == group) {
|
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, group->next);
|
||||
} else {
|
||||
/* look for group further down the list */
|
||||
struct igmp_group *tmpGroup;
|
||||
for (tmpGroup = netif_igmp_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) {
|
||||
if (tmpGroup->next == group) {
|
||||
tmpGroup->next = group->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Group not found in the global igmp_group_list */
|
||||
if (tmpGroup == NULL) {
|
||||
err = ERR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from ip_input() if a new IGMP packet is received.
|
||||
*
|
||||
* @param p received igmp packet, p->payload pointing to the igmp header
|
||||
* @param inp network interface on which the packet was received
|
||||
* @param dest destination ip address of the igmp packet
|
||||
*/
|
||||
void
|
||||
igmp_input(struct pbuf *p, struct netif *inp, const ip4_addr_t *dest)
|
||||
{
|
||||
struct igmp_msg* igmp;
|
||||
struct igmp_group* group;
|
||||
struct igmp_group* groupref;
|
||||
|
||||
IGMP_STATS_INC(igmp.recv);
|
||||
|
||||
/* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
|
||||
if (p->len < IGMP_MINLEN) {
|
||||
pbuf_free(p);
|
||||
IGMP_STATS_INC(igmp.lenerr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->src));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->dest));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)inp));
|
||||
|
||||
/* Now calculate and check the checksum */
|
||||
igmp = (struct igmp_msg *)p->payload;
|
||||
if (inet_chksum(igmp, p->len)) {
|
||||
pbuf_free(p);
|
||||
IGMP_STATS_INC(igmp.chkerr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Packet is ok so find an existing group */
|
||||
group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
|
||||
|
||||
/* If group can be found or create... */
|
||||
if (!group) {
|
||||
pbuf_free(p);
|
||||
IGMP_STATS_INC(igmp.drop);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* NOW ACT ON THE INCOMING MESSAGE TYPE... */
|
||||
switch (igmp->igmp_msgtype) {
|
||||
case IGMP_MEMB_QUERY:
|
||||
/* IGMP_MEMB_QUERY to the "all systems" address ? */
|
||||
if ((ip4_addr_cmp(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) {
|
||||
/* THIS IS THE GENERAL QUERY */
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
|
||||
|
||||
if (igmp->igmp_maxresp == 0) {
|
||||
IGMP_STATS_INC(igmp.rx_v1);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
|
||||
igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
|
||||
} else {
|
||||
IGMP_STATS_INC(igmp.rx_general);
|
||||
}
|
||||
|
||||
groupref = netif_igmp_data(inp);
|
||||
|
||||
/* Do not send messages on the all systems group address! */
|
||||
/* Skip the first group in the list, it is always the allsystems group added in igmp_start() */
|
||||
if(groupref != NULL) {
|
||||
groupref = groupref->next;
|
||||
}
|
||||
|
||||
while (groupref) {
|
||||
igmp_delaying_member(groupref, igmp->igmp_maxresp);
|
||||
groupref = groupref->next;
|
||||
}
|
||||
} else {
|
||||
/* IGMP_MEMB_QUERY to a specific group ? */
|
||||
if (!ip4_addr_isany(&igmp->igmp_group_address)) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
|
||||
if (ip4_addr_cmp(dest, &allsystems)) {
|
||||
ip4_addr_t groupaddr;
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
|
||||
/* we first need to re-look for the group since we used dest last time */
|
||||
ip4_addr_copy(groupaddr, igmp->igmp_group_address);
|
||||
group = igmp_lookfor_group(inp, &groupaddr);
|
||||
} else {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
|
||||
}
|
||||
|
||||
if (group != NULL) {
|
||||
IGMP_STATS_INC(igmp.rx_group);
|
||||
igmp_delaying_member(group, igmp->igmp_maxresp);
|
||||
} else {
|
||||
IGMP_STATS_INC(igmp.drop);
|
||||
}
|
||||
} else {
|
||||
IGMP_STATS_INC(igmp.proterr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IGMP_V2_MEMB_REPORT:
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
|
||||
IGMP_STATS_INC(igmp.rx_report);
|
||||
if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
|
||||
/* This is on a specific group we have already looked up */
|
||||
group->timer = 0; /* stopped */
|
||||
group->group_state = IGMP_GROUP_IDLE_MEMBER;
|
||||
group->last_reporter_flag = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
|
||||
igmp->igmp_msgtype, group->group_state, (void*)&group, (void*)inp));
|
||||
IGMP_STATS_INC(igmp.proterr);
|
||||
break;
|
||||
}
|
||||
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup igmp
|
||||
* Join a group on one network interface.
|
||||
*
|
||||
* @param ifaddr ip address of the network interface which should join a new group
|
||||
* @param groupaddr the ip address of the group which to join
|
||||
* @return ERR_OK if group was joined on the netif(s), an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
|
||||
{
|
||||
err_t err = ERR_VAL; /* no matching interface */
|
||||
struct netif *netif;
|
||||
|
||||
/* make sure it is multicast address */
|
||||
LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
|
||||
|
||||
/* loop through netif's */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
/* Should we join this interface ? */
|
||||
if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
|
||||
err = igmp_joingroup_netif(netif, groupaddr);
|
||||
if (err != ERR_OK) {
|
||||
/* Return an error even if some network interfaces are joined */
|
||||
/** @todo undo any other netif already joined */
|
||||
return err;
|
||||
}
|
||||
}
|
||||
/* proceed to next network interface */
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup igmp
|
||||
* Join a group on one network interface.
|
||||
*
|
||||
* @param netif the network interface which should join a new group
|
||||
* @param groupaddr the ip address of the group which to join
|
||||
* @return ERR_OK if group was joined on the netif, an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
igmp_joingroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
|
||||
{
|
||||
struct igmp_group *group;
|
||||
|
||||
/* make sure it is multicast address */
|
||||
LWIP_ERROR("igmp_joingroup_netif: attempt to join non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_joingroup_netif: attempt to join allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
|
||||
|
||||
/* make sure it is an igmp-enabled netif */
|
||||
LWIP_ERROR("igmp_joingroup_netif: attempt to join on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
|
||||
|
||||
/* find group or create a new one if not found */
|
||||
group = igmp_lookup_group(netif, groupaddr);
|
||||
|
||||
if (group != NULL) {
|
||||
/* This should create a new group, check the state to make sure */
|
||||
if (group->group_state != IGMP_GROUP_NON_MEMBER) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
|
||||
} else {
|
||||
/* OK - it was new group */
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: join to new group: "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
|
||||
|
||||
/* If first use of the group, allow the group at the MAC level */
|
||||
if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: igmp_mac_filter(ADD "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
|
||||
netif->igmp_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
|
||||
}
|
||||
|
||||
IGMP_STATS_INC(igmp.tx_join);
|
||||
igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
|
||||
|
||||
igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
|
||||
|
||||
/* Need to work out where this timer comes from */
|
||||
group->group_state = IGMP_GROUP_DELAYING_MEMBER;
|
||||
}
|
||||
/* Increment group use */
|
||||
group->use++;
|
||||
/* Join on this interface */
|
||||
return ERR_OK;
|
||||
} else {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup_netif: Not enough memory to join to group\n"));
|
||||
return ERR_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup igmp
|
||||
* Leave a group on one network interface.
|
||||
*
|
||||
* @param ifaddr ip address of the network interface which should leave a group
|
||||
* @param groupaddr the ip address of the group which to leave
|
||||
* @return ERR_OK if group was left on the netif(s), an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
|
||||
{
|
||||
err_t err = ERR_VAL; /* no matching interface */
|
||||
struct netif *netif;
|
||||
|
||||
/* make sure it is multicast address */
|
||||
LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
|
||||
|
||||
/* loop through netif's */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
/* Should we leave this interface ? */
|
||||
if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ifaddr) || ip4_addr_cmp(netif_ip4_addr(netif), ifaddr)))) {
|
||||
err_t res = igmp_leavegroup_netif(netif, groupaddr);
|
||||
if (err != ERR_OK) {
|
||||
/* Store this result if we have not yet gotten a success */
|
||||
err = res;
|
||||
}
|
||||
}
|
||||
/* proceed to next network interface */
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup igmp
|
||||
* Leave a group on one network interface.
|
||||
*
|
||||
* @param netif the network interface which should leave a group
|
||||
* @param groupaddr the ip address of the group which to leave
|
||||
* @return ERR_OK if group was left on the netif, an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
igmp_leavegroup_netif(struct netif *netif, const ip4_addr_t *groupaddr)
|
||||
{
|
||||
struct igmp_group *group;
|
||||
|
||||
/* make sure it is multicast address */
|
||||
LWIP_ERROR("igmp_leavegroup_netif: attempt to leave non-multicast address", ip4_addr_ismulticast(groupaddr), return ERR_VAL;);
|
||||
LWIP_ERROR("igmp_leavegroup_netif: attempt to leave allsystems address", (!ip4_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
|
||||
|
||||
/* make sure it is an igmp-enabled netif */
|
||||
LWIP_ERROR("igmp_leavegroup_netif: attempt to leave on non-IGMP netif", netif->flags & NETIF_FLAG_IGMP, return ERR_VAL;);
|
||||
|
||||
/* find group */
|
||||
group = igmp_lookfor_group(netif, groupaddr);
|
||||
|
||||
if (group != NULL) {
|
||||
/* Only send a leave if the flag is set according to the state diagram */
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: Leaving group: "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
|
||||
|
||||
/* If there is no other use of the group */
|
||||
if (group->use <= 1) {
|
||||
/* Remove the group from the list */
|
||||
igmp_remove_group(netif, group);
|
||||
|
||||
/* If we are the last reporter for this group */
|
||||
if (group->last_reporter_flag) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: sending leaving group\n"));
|
||||
IGMP_STATS_INC(igmp.tx_leave);
|
||||
igmp_send(netif, group, IGMP_LEAVE_GROUP);
|
||||
}
|
||||
|
||||
/* Disable the group at the MAC level */
|
||||
if (netif->igmp_mac_filter != NULL) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, groupaddr);
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", (void*)netif));
|
||||
netif->igmp_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
|
||||
}
|
||||
|
||||
/* Free group struct */
|
||||
memp_free(MEMP_IGMP_GROUP, group);
|
||||
} else {
|
||||
/* Decrement group use */
|
||||
group->use--;
|
||||
}
|
||||
return ERR_OK;
|
||||
} else {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: not member of group\n"));
|
||||
return ERR_VAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The igmp timer function (both for NO_SYS=1 and =0)
|
||||
* Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
|
||||
*/
|
||||
void
|
||||
igmp_tmr(void)
|
||||
{
|
||||
struct netif *netif = netif_list;
|
||||
|
||||
while (netif != NULL) {
|
||||
struct igmp_group *group = netif_igmp_data(netif);
|
||||
|
||||
while (group != NULL) {
|
||||
if (group->timer > 0) {
|
||||
group->timer--;
|
||||
if (group->timer == 0) {
|
||||
igmp_timeout(netif, group);
|
||||
}
|
||||
}
|
||||
group = group->next;
|
||||
}
|
||||
netif = netif->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called if a timeout for one group is reached.
|
||||
* Sends a report for this group.
|
||||
*
|
||||
* @param group an igmp_group for which a timeout is reached
|
||||
*/
|
||||
static void
|
||||
igmp_timeout(struct netif *netif, struct igmp_group *group)
|
||||
{
|
||||
/* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group
|
||||
(unless it is the allsystems group) */
|
||||
if ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
|
||||
(!(ip4_addr_cmp(&(group->group_address), &allsystems)))) {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
|
||||
ip4_addr_debug_print(IGMP_DEBUG, &(group->group_address));
|
||||
LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void*)netif));
|
||||
|
||||
group->group_state = IGMP_GROUP_IDLE_MEMBER;
|
||||
|
||||
IGMP_STATS_INC(igmp.tx_report);
|
||||
igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a timer for an igmp group
|
||||
*
|
||||
* @param group the igmp_group for which to start a timer
|
||||
* @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
|
||||
* every call to igmp_tmr())
|
||||
*/
|
||||
static void
|
||||
igmp_start_timer(struct igmp_group *group, u8_t max_time)
|
||||
{
|
||||
#ifdef LWIP_RAND
|
||||
group->timer = max_time > 2 ? (LWIP_RAND() % max_time) : 1;
|
||||
#else /* LWIP_RAND */
|
||||
/* ATTENTION: use this only if absolutely necessary! */
|
||||
group->timer = max_time / 2;
|
||||
#endif /* LWIP_RAND */
|
||||
|
||||
if (group->timer == 0) {
|
||||
group->timer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delaying membership report for a group if necessary
|
||||
*
|
||||
* @param group the igmp_group for which "delaying" membership report
|
||||
* @param maxresp query delay
|
||||
*/
|
||||
static void
|
||||
igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
|
||||
{
|
||||
if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
|
||||
((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
|
||||
((group->timer == 0) || (maxresp < group->timer)))) {
|
||||
igmp_start_timer(group, maxresp);
|
||||
group->group_state = IGMP_GROUP_DELAYING_MEMBER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sends an IP packet on a network interface. This function constructs the IP header
|
||||
* and calculates the IP header checksum. If the source IP address is NULL,
|
||||
* the IP address of the outgoing network interface is filled in as source address.
|
||||
*
|
||||
* @param p the packet to send (p->payload points to the data, e.g. next
|
||||
protocol header; if dest == LWIP_IP_HDRINCL, p already includes an
|
||||
IP header and p->payload points to that IP header)
|
||||
* @param src the source IP address to send from (if src == IP4_ADDR_ANY, the
|
||||
* IP address of the netif used to send is used as source address)
|
||||
* @param dest the destination IP address to send the packet to
|
||||
* @param netif the netif on which to send this packet
|
||||
* @return ERR_OK if the packet was sent OK
|
||||
* ERR_BUF if p doesn't have enough space for IP/LINK headers
|
||||
* returns errors returned by netif->output
|
||||
*/
|
||||
static err_t
|
||||
igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif)
|
||||
{
|
||||
/* This is the "router alert" option */
|
||||
u16_t ra[2];
|
||||
ra[0] = PP_HTONS(ROUTER_ALERT);
|
||||
ra[1] = 0x0000; /* Router shall examine packet */
|
||||
IGMP_STATS_INC(igmp.xmit);
|
||||
return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an igmp packet to a specific group.
|
||||
*
|
||||
* @param group the group to which to send the packet
|
||||
* @param type the type of igmp packet to send
|
||||
*/
|
||||
static void
|
||||
igmp_send(struct netif *netif, struct igmp_group *group, u8_t type)
|
||||
{
|
||||
struct pbuf* p = NULL;
|
||||
struct igmp_msg* igmp = NULL;
|
||||
ip4_addr_t src = *IP4_ADDR_ANY4;
|
||||
ip4_addr_t* dest = NULL;
|
||||
|
||||
/* IP header + "router alert" option + IGMP header */
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
|
||||
|
||||
if (p) {
|
||||
igmp = (struct igmp_msg *)p->payload;
|
||||
LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
|
||||
(p->len >= sizeof(struct igmp_msg)));
|
||||
ip4_addr_copy(src, *netif_ip4_addr(netif));
|
||||
|
||||
if (type == IGMP_V2_MEMB_REPORT) {
|
||||
dest = &(group->group_address);
|
||||
ip4_addr_copy(igmp->igmp_group_address, group->group_address);
|
||||
group->last_reporter_flag = 1; /* Remember we were the last to report */
|
||||
} else {
|
||||
if (type == IGMP_LEAVE_GROUP) {
|
||||
dest = &allrouters;
|
||||
ip4_addr_copy(igmp->igmp_group_address, group->group_address);
|
||||
}
|
||||
}
|
||||
|
||||
if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
|
||||
igmp->igmp_msgtype = type;
|
||||
igmp->igmp_maxresp = 0;
|
||||
igmp->igmp_checksum = 0;
|
||||
igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
|
||||
|
||||
igmp_ip_output_if(p, &src, dest, netif);
|
||||
}
|
||||
|
||||
pbuf_free(p);
|
||||
} else {
|
||||
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
|
||||
IGMP_STATS_INC(igmp.memerr);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 && LWIP_IGMP */
|
||||
@@ -1,581 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* ip.c
|
||||
*
|
||||
* This is the code for the IP layer.
|
||||
*
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/ip_frag.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/icmp.h"
|
||||
#include "lwip/udp.h"
|
||||
#include "lwip/tcp.h"
|
||||
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include "arch/perf.h"
|
||||
|
||||
#include "lwip/snmp.h"
|
||||
#if LWIP_DHCP
|
||||
# include "lwip/dhcp.h"
|
||||
#endif /* LWIP_DHCP */
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* ip_init:
|
||||
*
|
||||
* Initializes the IP layer.
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void
|
||||
ip_init(void)
|
||||
{
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* ip_lookup:
|
||||
*
|
||||
* An experimental feature that will be changed in future versions. Do
|
||||
* not depend on it yet...
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
#ifdef LWIP_DEBUG
|
||||
u8_t
|
||||
ip_lookup(void *header, struct netif *inp)
|
||||
{
|
||||
struct ip_hdr *iphdr;
|
||||
|
||||
iphdr = header;
|
||||
|
||||
/* not IP v4? */
|
||||
if (IPH_V(iphdr) != 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Immediately accept/decline packets that are fragments or has
|
||||
options. */
|
||||
#if IP_REASSEMBLY == 0
|
||||
/* if ((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) {
|
||||
return 0;
|
||||
}*/
|
||||
#endif /* IP_REASSEMBLY == 0 */
|
||||
|
||||
#if IP_OPTIONS == 0
|
||||
if (IPH_HL(iphdr) != 5) {
|
||||
return 0;
|
||||
}
|
||||
#endif /* IP_OPTIONS == 0 */
|
||||
|
||||
switch (IPH_PROTO(iphdr)) {
|
||||
#if LWIP_UDP > 0
|
||||
case IP_PROTO_UDP:
|
||||
return udp_lookup(iphdr, inp);
|
||||
#endif /* LWIP_UDP */
|
||||
#if LWIP_TCP > 0
|
||||
case IP_PROTO_TCP:
|
||||
return 1;
|
||||
#endif /* LWIP_TCP */
|
||||
case IP_PROTO_ICMP:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif /* LWIP_DEBUG */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* ip_route:
|
||||
*
|
||||
* Finds the appropriate network interface for a given IP address. It
|
||||
* searches the list of network interfaces linearly. A match is found
|
||||
* if the masked IP address of the network interface equals the masked
|
||||
* IP address given to the function.
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
struct netif *
|
||||
ip_route(struct ip_addr *dest)
|
||||
{
|
||||
struct netif *netif;
|
||||
|
||||
/* iterate through netifs */
|
||||
for(netif = netif_list; netif != NULL; netif = netif->next) {
|
||||
/* network mask matches? */
|
||||
if (ip_addr_maskcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
|
||||
/* return netif on which to forward IP packet */
|
||||
return netif;
|
||||
}
|
||||
}
|
||||
/* no matching netif found, use default netif */
|
||||
return netif_default;
|
||||
}
|
||||
#if IP_FORWARD
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* ip_forward:
|
||||
*
|
||||
* Forwards an IP packet. It finds an appropriate route for the
|
||||
* packet, decrements the TTL value of the packet, adjusts the
|
||||
* checksum and outputs the packet on the appropriate interface.
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
static void
|
||||
ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
|
||||
{
|
||||
struct netif *netif;
|
||||
|
||||
PERF_START;
|
||||
/* Find network interface where to forward this IP packet to. */
|
||||
netif = ip_route((struct ip_addr *)&(iphdr->dest));
|
||||
if (netif == NULL) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for 0x%lx found\n",
|
||||
iphdr->dest.addr));
|
||||
snmp_inc_ipnoroutes();
|
||||
return;
|
||||
}
|
||||
/* Do not forward packets onto the same network interface on which
|
||||
they arrived. */
|
||||
if (netif == inp) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n"));
|
||||
snmp_inc_ipnoroutes();
|
||||
return;
|
||||
}
|
||||
|
||||
/* decrement TTL */
|
||||
IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
|
||||
/* send ICMP if TTL == 0 */
|
||||
if (IPH_TTL(iphdr) == 0) {
|
||||
/* Don't send ICMP messages in response to ICMP messages */
|
||||
if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
|
||||
icmp_time_exceeded(p, ICMP_TE_TTL);
|
||||
snmp_inc_icmpouttimeexcds();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Incrementally update the IP checksum. */
|
||||
if (IPH_CHKSUM(iphdr) >= htons(0xffff - 0x100)) {
|
||||
IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100) + 1);
|
||||
} else {
|
||||
IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100));
|
||||
}
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to 0x%lx\n",
|
||||
iphdr->dest.addr));
|
||||
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip.fw;
|
||||
++lwip_stats.ip.xmit;
|
||||
#endif /* IP_STATS */
|
||||
snmp_inc_ipforwdatagrams();
|
||||
|
||||
PERF_STOP("ip_forward");
|
||||
/* transmit pbuf on chosen interface */
|
||||
netif->output(netif, p, (struct ip_addr *)&(iphdr->dest));
|
||||
}
|
||||
#endif /* IP_FORWARD */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* ip_input:
|
||||
*
|
||||
* This function is called by the network interface device driver when
|
||||
* an IP packet is received. The function does the basic checks of the
|
||||
* IP header such as packet size being at least larger than the header
|
||||
* size etc. If the packet was not destined for us, the packet is
|
||||
* forwarded (using ip_forward). The IP checksum is always checked.
|
||||
*
|
||||
* Finally, the packet is sent to the upper layer protocol input function.
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
err_t
|
||||
ip_input(struct pbuf *p, struct netif *inp) {
|
||||
static struct ip_hdr *iphdr;
|
||||
static struct netif *netif;
|
||||
static u16_t iphdrlen;
|
||||
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip.recv;
|
||||
#endif /* IP_STATS */
|
||||
snmp_inc_ipinreceives();
|
||||
|
||||
/* identify the IP header */
|
||||
iphdr = p->payload;
|
||||
if (IPH_V(iphdr) != 4) {
|
||||
LWIP_DEBUGF(IP_DEBUG | 1, ("IP packet dropped due to bad version number %u\n", IPH_V(iphdr)));
|
||||
#if IP_DEBUG
|
||||
ip_debug_print(p);
|
||||
#endif /* IP_DEBUG */
|
||||
pbuf_free(p);
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip.err;
|
||||
++lwip_stats.ip.drop;
|
||||
#endif /* IP_STATS */
|
||||
snmp_inc_ipunknownprotos();
|
||||
return ERR_OK;
|
||||
}
|
||||
/* obtain IP header length in number of 32-bit words */
|
||||
iphdrlen = IPH_HL(iphdr);
|
||||
/* calculate IP header length in bytes */
|
||||
iphdrlen *= 4;
|
||||
|
||||
/* header length exceeds first pbuf length? */
|
||||
if (iphdrlen > p->len) {
|
||||
LWIP_DEBUGF(IP_DEBUG | 2, ("IP header (len %u) does not fit in first pbuf (len %u), IP packet droppped.\n",
|
||||
iphdrlen, p->len));
|
||||
/* free (drop) packet pbufs */
|
||||
pbuf_free(p);
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip.lenerr;
|
||||
++lwip_stats.ip.drop;
|
||||
#endif /* IP_STATS */
|
||||
snmp_inc_ipindiscards();
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/* verify checksum */
|
||||
if (inet_chksum(iphdr, iphdrlen) != 0) {
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG | 2, ("Checksum (0x%x) failed, IP packet dropped.\n", inet_chksum(iphdr, iphdrlen)));
|
||||
#if IP_DEBUG
|
||||
ip_debug_print(p);
|
||||
#endif /* IP_DEBUG */
|
||||
pbuf_free(p);
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip.chkerr;
|
||||
++lwip_stats.ip.drop;
|
||||
#endif /* IP_STATS */
|
||||
snmp_inc_ipindiscards();
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/* Trim pbuf. This should have been done at the netif layer,
|
||||
but we'll do it anyway just to be sure that its done. */
|
||||
pbuf_realloc(p, ntohs(IPH_LEN(iphdr)));
|
||||
|
||||
/* is this packet for us? */
|
||||
for(netif = netif_list; netif != NULL; netif = netif->next) {
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%lx netif->ip_addr 0x%lx (0x%lx, 0x%lx, 0x%lx)\n",
|
||||
iphdr->dest.addr, netif->ip_addr.addr,
|
||||
iphdr->dest.addr & netif->netmask.addr,
|
||||
netif->ip_addr.addr & netif->netmask.addr,
|
||||
iphdr->dest.addr & ~(netif->netmask.addr)));
|
||||
|
||||
/* interface configured? */
|
||||
if (!ip_addr_isany(&(netif->ip_addr)))
|
||||
{
|
||||
/* unicast to this interface address? */
|
||||
if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) ||
|
||||
/* or broadcast matching this interface network address? */
|
||||
(ip_addr_isbroadcast(&(iphdr->dest), &(netif->netmask)) &&
|
||||
ip_addr_maskcmp(&(iphdr->dest), &(netif->ip_addr), &(netif->netmask))) ||
|
||||
/* or restricted broadcast? */
|
||||
ip_addr_cmp(&(iphdr->dest), IP_ADDR_BROADCAST)) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n",
|
||||
netif->name[0], netif->name[1]));
|
||||
/* break out of for loop */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#if LWIP_DHCP
|
||||
/* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
|
||||
using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
|
||||
According to RFC 1542 section 3.1.1, referred by RFC 2131). */
|
||||
if (netif == NULL) {
|
||||
/* remote port is DHCP server? */
|
||||
if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
|
||||
LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: UDP packet to DHCP client port %u\n",
|
||||
ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest)));
|
||||
if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest) == DHCP_CLIENT_PORT) {
|
||||
LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: DHCP packet accepted.\n"));
|
||||
netif = inp;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* LWIP_DHCP */
|
||||
/* packet not for us? */
|
||||
if (netif == NULL) {
|
||||
/* packet not for us, route or discard */
|
||||
LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: packet not for us.\n"));
|
||||
#if IP_FORWARD
|
||||
/* non-broadcast packet? */
|
||||
if (!ip_addr_isbroadcast(&(iphdr->dest), &(inp->netmask))) {
|
||||
/* try to forward IP packet on (other) interfaces */
|
||||
ip_forward(p, iphdr, inp);
|
||||
}
|
||||
else
|
||||
#endif /* IP_FORWARD */
|
||||
{
|
||||
snmp_inc_ipindiscards();
|
||||
}
|
||||
pbuf_free(p);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
#if IP_REASSEMBLY
|
||||
if ((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) {
|
||||
LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04x tot_len=%u len=%u MF=%u offset=%u), calling ip_reass()\n",
|
||||
ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & htons(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8));
|
||||
p = ip_reass(p);
|
||||
if (p == NULL) {
|
||||
return ERR_OK;
|
||||
}
|
||||
iphdr = p->payload;
|
||||
}
|
||||
#else /* IP_REASSEMBLY */
|
||||
if ((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) {
|
||||
pbuf_free(p);
|
||||
LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since it was fragmented (0x%x) (while IP_REASSEMBLY == 0).\n",
|
||||
ntohs(IPH_OFFSET(iphdr))));
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip.opterr;
|
||||
++lwip_stats.ip.drop;
|
||||
#endif /* IP_STATS */
|
||||
snmp_inc_ipunknownprotos();
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* IP_REASSEMBLY */
|
||||
|
||||
#if IP_OPTIONS == 0
|
||||
if (iphdrlen > IP_HLEN) {
|
||||
LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since there were IP options (while IP_OPTIONS == 0).\n"));
|
||||
pbuf_free(p);
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip.opterr;
|
||||
++lwip_stats.ip.drop;
|
||||
#endif /* IP_STATS */
|
||||
snmp_inc_ipunknownprotos();
|
||||
return ERR_OK;
|
||||
}
|
||||
#endif /* IP_OPTIONS == 0 */
|
||||
|
||||
/* send to upper layers */
|
||||
#if IP_DEBUG
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n"));
|
||||
ip_debug_print(p);
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %d p->tot_len %d\n", p->len, p->tot_len));
|
||||
#endif /* IP_DEBUG */
|
||||
|
||||
switch (IPH_PROTO(iphdr)) {
|
||||
#if LWIP_UDP > 0
|
||||
case IP_PROTO_UDP:
|
||||
snmp_inc_ipindelivers();
|
||||
udp_input(p, inp);
|
||||
break;
|
||||
#endif /* LWIP_UDP */
|
||||
#if LWIP_TCP > 0
|
||||
case IP_PROTO_TCP:
|
||||
snmp_inc_ipindelivers();
|
||||
tcp_input(p, inp);
|
||||
break;
|
||||
#endif /* LWIP_TCP */
|
||||
case IP_PROTO_ICMP:
|
||||
snmp_inc_ipindelivers();
|
||||
icmp_input(p, inp);
|
||||
break;
|
||||
default:
|
||||
/* send ICMP destination protocol unreachable unless is was a broadcast */
|
||||
if (!ip_addr_isbroadcast(&(iphdr->dest), &(inp->netmask)) &&
|
||||
!ip_addr_ismulticast(&(iphdr->dest))) {
|
||||
p->payload = iphdr;
|
||||
icmp_dest_unreach(p, ICMP_DUR_PROTO);
|
||||
}
|
||||
pbuf_free(p);
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG | 2, ("Unsupported transport protocol %d\n", IPH_PROTO(iphdr)));
|
||||
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip.proterr;
|
||||
++lwip_stats.ip.drop;
|
||||
#endif /* IP_STATS */
|
||||
snmp_inc_ipunknownprotos();
|
||||
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* ip_output_if:
|
||||
*
|
||||
* Sends an IP packet on a network interface. This function constructs
|
||||
* the IP header and calculates the IP header checksum. If the source
|
||||
* IP address is NULL, the IP address of the outgoing network
|
||||
* interface is filled in as source address.
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
err_t
|
||||
ip_output_if (struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
|
||||
u8_t ttl,
|
||||
u8_t proto, struct netif *netif)
|
||||
{
|
||||
static struct ip_hdr *iphdr;
|
||||
static u16_t ip_id = 0;
|
||||
|
||||
snmp_inc_ipoutrequests();
|
||||
|
||||
if (dest != IP_HDRINCL) {
|
||||
if (pbuf_header(p, IP_HLEN)) {
|
||||
LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: not enough room for IP header in pbuf\n"));
|
||||
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip.err;
|
||||
#endif /* IP_STATS */
|
||||
snmp_inc_ipoutdiscards();
|
||||
return ERR_BUF;
|
||||
}
|
||||
|
||||
iphdr = p->payload;
|
||||
|
||||
IPH_TTL_SET(iphdr, ttl);
|
||||
IPH_PROTO_SET(iphdr, proto);
|
||||
|
||||
ip_addr_set(&(iphdr->dest), dest);
|
||||
|
||||
IPH_VHLTOS_SET(iphdr, 4, IP_HLEN / 4, 0);
|
||||
IPH_LEN_SET(iphdr, htons(p->tot_len));
|
||||
IPH_OFFSET_SET(iphdr, htons(IP_DF));
|
||||
IPH_ID_SET(iphdr, htons(ip_id));
|
||||
++ip_id;
|
||||
|
||||
if (ip_addr_isany(src)) {
|
||||
ip_addr_set(&(iphdr->src), &(netif->ip_addr));
|
||||
} else {
|
||||
ip_addr_set(&(iphdr->src), src);
|
||||
}
|
||||
|
||||
IPH_CHKSUM_SET(iphdr, 0);
|
||||
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
|
||||
} else {
|
||||
iphdr = p->payload;
|
||||
dest = &(iphdr->dest);
|
||||
}
|
||||
|
||||
#if IP_FRAG
|
||||
/* don't fragment if interface has mtu set to 0 [loopif] */
|
||||
if (netif->mtu && (p->tot_len > netif->mtu))
|
||||
return ip_frag(p,netif,dest);
|
||||
#endif
|
||||
|
||||
#ifdef IP_STATS
|
||||
lwip_stats.ip.xmit++;
|
||||
#endif /* IP_STATS */
|
||||
LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%u\n", netif->name[0], netif->name[1], netif->num));
|
||||
#if IP_DEBUG
|
||||
ip_debug_print(p);
|
||||
#endif /* IP_DEBUG */
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG, ("netif->output()"));
|
||||
|
||||
return netif->output(netif, p, dest);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* ip_output:
|
||||
*
|
||||
* Simple interface to ip_output_if. It finds the outgoing network
|
||||
* interface and calls upon ip_output_if to do the actual work.
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
err_t
|
||||
ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest,
|
||||
u8_t ttl, u8_t proto)
|
||||
{
|
||||
struct netif *netif;
|
||||
|
||||
if ((netif = ip_route(dest)) == NULL) {
|
||||
LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: No route to 0x%lx\n", dest->addr));
|
||||
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip.rterr;
|
||||
#endif /* IP_STATS */
|
||||
snmp_inc_ipoutdiscards();
|
||||
return ERR_RTE;
|
||||
}
|
||||
|
||||
return ip_output_if (p, src, dest, ttl, proto, netif);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
#if IP_DEBUG
|
||||
void
|
||||
ip_debug_print(struct pbuf *p)
|
||||
{
|
||||
struct ip_hdr *iphdr = p->payload;
|
||||
u8_t *payload;
|
||||
|
||||
payload = (u8_t *)iphdr + IP_HLEN;
|
||||
|
||||
LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("|%2d |%2d | 0x%02x | %5u | (v, hl, tos, len)\n",
|
||||
IPH_V(iphdr),
|
||||
IPH_HL(iphdr),
|
||||
IPH_TOS(iphdr),
|
||||
ntohs(IPH_LEN(iphdr))));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("| %5u |%u%u%u| %4u | (id, flags, offset)\n",
|
||||
ntohs(IPH_ID(iphdr)),
|
||||
ntohs(IPH_OFFSET(iphdr)) >> 15 & 1,
|
||||
ntohs(IPH_OFFSET(iphdr)) >> 14 & 1,
|
||||
ntohs(IPH_OFFSET(iphdr)) >> 13 & 1,
|
||||
ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("| %3u | %3u | 0x%04x | (ttl, proto, chksum)\n",
|
||||
IPH_TTL(iphdr),
|
||||
IPH_PROTO(iphdr),
|
||||
ntohs(IPH_CHKSUM(iphdr))));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("| %3ld | %3ld | %3ld | %3ld | (src)\n",
|
||||
ntohl(iphdr->src.addr) >> 24 & 0xff,
|
||||
ntohl(iphdr->src.addr) >> 16 & 0xff,
|
||||
ntohl(iphdr->src.addr) >> 8 & 0xff,
|
||||
ntohl(iphdr->src.addr) & 0xff));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("| %3ld | %3ld | %3ld | %3ld | (dest)\n",
|
||||
ntohl(iphdr->dest.addr) >> 24 & 0xff,
|
||||
ntohl(iphdr->dest.addr) >> 16 & 0xff,
|
||||
ntohl(iphdr->dest.addr) >> 8 & 0xff,
|
||||
ntohl(iphdr->dest.addr) & 0xff));
|
||||
LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
|
||||
}
|
||||
#endif /* IP_DEBUG */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
1072
src/core/ipv4/ip4.c
Normal file
1072
src/core/ipv4/ip4.c
Normal file
File diff suppressed because it is too large
Load Diff
331
src/core/ipv4/ip4_addr.c
Normal file
331
src/core/ipv4/ip4_addr.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/**
|
||||
* @file
|
||||
* This is the IPv4 address tools implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV4
|
||||
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
/* used by IP4_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
|
||||
const ip_addr_t ip_addr_any = IPADDR4_INIT(IPADDR_ANY);
|
||||
const ip_addr_t ip_addr_broadcast = IPADDR4_INIT(IPADDR_BROADCAST);
|
||||
|
||||
/**
|
||||
* Determine if an address is a broadcast address on a network interface
|
||||
*
|
||||
* @param addr address to be checked
|
||||
* @param netif the network interface against which the address is checked
|
||||
* @return returns non-zero if the address is a broadcast address
|
||||
*/
|
||||
u8_t
|
||||
ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif)
|
||||
{
|
||||
ip4_addr_t ipaddr;
|
||||
ip4_addr_set_u32(&ipaddr, addr);
|
||||
|
||||
/* all ones (broadcast) or all zeroes (old skool broadcast) */
|
||||
if ((~addr == IPADDR_ANY) ||
|
||||
(addr == IPADDR_ANY)) {
|
||||
return 1;
|
||||
/* no broadcast support on this network interface? */
|
||||
} else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) {
|
||||
/* the given address cannot be a broadcast address
|
||||
* nor can we check against any broadcast addresses */
|
||||
return 0;
|
||||
/* address matches network interface address exactly? => no broadcast */
|
||||
} else if (addr == ip4_addr_get_u32(netif_ip4_addr(netif))) {
|
||||
return 0;
|
||||
/* on the same (sub) network... */
|
||||
} else if (ip4_addr_netcmp(&ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif))
|
||||
/* ...and host identifier bits are all ones? =>... */
|
||||
&& ((addr & ~ip4_addr_get_u32(netif_ip4_netmask(netif))) ==
|
||||
(IPADDR_BROADCAST & ~ip4_addr_get_u32(netif_ip4_netmask(netif))))) {
|
||||
/* => network broadcast address */
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks if a netmask is valid (starting with ones, then only zeros)
|
||||
*
|
||||
* @param netmask the IPv4 netmask to check (in network byte order!)
|
||||
* @return 1 if the netmask is valid, 0 if it is not
|
||||
*/
|
||||
u8_t
|
||||
ip4_addr_netmask_valid(u32_t netmask)
|
||||
{
|
||||
u32_t mask;
|
||||
u32_t nm_hostorder = lwip_htonl(netmask);
|
||||
|
||||
/* first, check for the first zero */
|
||||
for (mask = 1UL << 31 ; mask != 0; mask >>= 1) {
|
||||
if ((nm_hostorder & mask) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* then check that there is no one */
|
||||
for (; mask != 0; mask >>= 1) {
|
||||
if ((nm_hostorder & mask) != 0) {
|
||||
/* there is a one after the first zero -> invalid */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* no one after the first zero -> valid */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Here for now until needed in other places in lwIP */
|
||||
#ifndef isprint
|
||||
#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up)
|
||||
#define isprint(c) in_range(c, 0x20, 0x7f)
|
||||
#define isdigit(c) in_range(c, '0', '9')
|
||||
#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
|
||||
#define islower(c) in_range(c, 'a', 'z')
|
||||
#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Ascii internet address interpretation routine.
|
||||
* The value returned is in network order.
|
||||
*
|
||||
* @param cp IP address in ascii representation (e.g. "127.0.0.1")
|
||||
* @return ip address in network order
|
||||
*/
|
||||
u32_t
|
||||
ipaddr_addr(const char *cp)
|
||||
{
|
||||
ip4_addr_t val;
|
||||
|
||||
if (ip4addr_aton(cp, &val)) {
|
||||
return ip4_addr_get_u32(&val);
|
||||
}
|
||||
return (IPADDR_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether "cp" is a valid ascii representation
|
||||
* of an Internet address and convert to a binary address.
|
||||
* Returns 1 if the address is valid, 0 if not.
|
||||
* This replaces inet_addr, the return value from which
|
||||
* cannot distinguish between failure and a local broadcast address.
|
||||
*
|
||||
* @param cp IP address in ascii representation (e.g. "127.0.0.1")
|
||||
* @param addr pointer to which to save the ip address in network order
|
||||
* @return 1 if cp could be converted to addr, 0 on failure
|
||||
*/
|
||||
int
|
||||
ip4addr_aton(const char *cp, ip4_addr_t *addr)
|
||||
{
|
||||
u32_t val;
|
||||
u8_t base;
|
||||
char c;
|
||||
u32_t parts[4];
|
||||
u32_t *pp = parts;
|
||||
|
||||
c = *cp;
|
||||
for (;;) {
|
||||
/*
|
||||
* Collect number up to ``.''.
|
||||
* Values are specified as for C:
|
||||
* 0x=hex, 0=octal, 1-9=decimal.
|
||||
*/
|
||||
if (!isdigit(c)) {
|
||||
return 0;
|
||||
}
|
||||
val = 0;
|
||||
base = 10;
|
||||
if (c == '0') {
|
||||
c = *++cp;
|
||||
if (c == 'x' || c == 'X') {
|
||||
base = 16;
|
||||
c = *++cp;
|
||||
} else {
|
||||
base = 8;
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
if (isdigit(c)) {
|
||||
val = (val * base) + (int)(c - '0');
|
||||
c = *++cp;
|
||||
} else if (base == 16 && isxdigit(c)) {
|
||||
val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A'));
|
||||
c = *++cp;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c == '.') {
|
||||
/*
|
||||
* Internet format:
|
||||
* a.b.c.d
|
||||
* a.b.c (with c treated as 16 bits)
|
||||
* a.b (with b treated as 24 bits)
|
||||
*/
|
||||
if (pp >= parts + 3) {
|
||||
return 0;
|
||||
}
|
||||
*pp++ = val;
|
||||
c = *++cp;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Check for trailing characters.
|
||||
*/
|
||||
if (c != '\0' && !isspace(c)) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Concoct the address according to
|
||||
* the number of parts specified.
|
||||
*/
|
||||
switch (pp - parts + 1) {
|
||||
|
||||
case 0:
|
||||
return 0; /* initial nondigit */
|
||||
|
||||
case 1: /* a -- 32 bits */
|
||||
break;
|
||||
|
||||
case 2: /* a.b -- 8.24 bits */
|
||||
if (val > 0xffffffUL) {
|
||||
return 0;
|
||||
}
|
||||
if (parts[0] > 0xff) {
|
||||
return 0;
|
||||
}
|
||||
val |= parts[0] << 24;
|
||||
break;
|
||||
|
||||
case 3: /* a.b.c -- 8.8.16 bits */
|
||||
if (val > 0xffff) {
|
||||
return 0;
|
||||
}
|
||||
if ((parts[0] > 0xff) || (parts[1] > 0xff)) {
|
||||
return 0;
|
||||
}
|
||||
val |= (parts[0] << 24) | (parts[1] << 16);
|
||||
break;
|
||||
|
||||
case 4: /* a.b.c.d -- 8.8.8.8 bits */
|
||||
if (val > 0xff) {
|
||||
return 0;
|
||||
}
|
||||
if ((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff)) {
|
||||
return 0;
|
||||
}
|
||||
val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
|
||||
break;
|
||||
default:
|
||||
LWIP_ASSERT("unhandled", 0);
|
||||
break;
|
||||
}
|
||||
if (addr) {
|
||||
ip4_addr_set_u32(addr, lwip_htonl(val));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert numeric IP address into decimal dotted ASCII representation.
|
||||
* returns ptr to static buffer; not reentrant!
|
||||
*
|
||||
* @param addr ip address in network order to convert
|
||||
* @return pointer to a global static (!) buffer that holds the ASCII
|
||||
* representation of addr
|
||||
*/
|
||||
char*
|
||||
ip4addr_ntoa(const ip4_addr_t *addr)
|
||||
{
|
||||
static char str[IP4ADDR_STRLEN_MAX];
|
||||
return ip4addr_ntoa_r(addr, str, IP4ADDR_STRLEN_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
|
||||
*
|
||||
* @param addr ip address in network order to convert
|
||||
* @param buf target buffer where the string is stored
|
||||
* @param buflen length of buf
|
||||
* @return either pointer to buf which now holds the ASCII
|
||||
* representation of addr or NULL if buf was too small
|
||||
*/
|
||||
char*
|
||||
ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen)
|
||||
{
|
||||
u32_t s_addr;
|
||||
char inv[3];
|
||||
char *rp;
|
||||
u8_t *ap;
|
||||
u8_t rem;
|
||||
u8_t n;
|
||||
u8_t i;
|
||||
int len = 0;
|
||||
|
||||
s_addr = ip4_addr_get_u32(addr);
|
||||
|
||||
rp = buf;
|
||||
ap = (u8_t *)&s_addr;
|
||||
for (n = 0; n < 4; n++) {
|
||||
i = 0;
|
||||
do {
|
||||
rem = *ap % (u8_t)10;
|
||||
*ap /= (u8_t)10;
|
||||
inv[i++] = '0' + rem;
|
||||
} while (*ap);
|
||||
while (i--) {
|
||||
if (len++ >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
*rp++ = inv[i];
|
||||
}
|
||||
if (len++ >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
*rp++ = '.';
|
||||
ap++;
|
||||
}
|
||||
*--rp = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV4 */
|
||||
833
src/core/ipv4/ip4_frag.c
Normal file
833
src/core/ipv4/ip4_frag.c
Normal file
@@ -0,0 +1,833 @@
|
||||
/**
|
||||
* @file
|
||||
* This is the IPv4 packet segmentation and reassembly implementation.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Jani Monoses <jani@iv.ro>
|
||||
* Simon Goldschmidt
|
||||
* original reassembly code by Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV4
|
||||
|
||||
#include "lwip/ip4_frag.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/icmp.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if IP_REASSEMBLY
|
||||
/**
|
||||
* The IP reassembly code currently has the following limitations:
|
||||
* - IP header options are not supported
|
||||
* - fragments must not overlap (e.g. due to different routes),
|
||||
* currently, overlapping or duplicate fragments are thrown away
|
||||
* if IP_REASS_CHECK_OVERLAP=1 (the default)!
|
||||
*
|
||||
* @todo: work with IP header options
|
||||
*/
|
||||
|
||||
/** Setting this to 0, you can turn off checking the fragments for overlapping
|
||||
* regions. The code gets a little smaller. Only use this if you know that
|
||||
* overlapping won't occur on your network! */
|
||||
#ifndef IP_REASS_CHECK_OVERLAP
|
||||
#define IP_REASS_CHECK_OVERLAP 1
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
|
||||
/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
|
||||
* full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
|
||||
* Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
|
||||
* is set to 1, so one datagram can be reassembled at a time, only. */
|
||||
#ifndef IP_REASS_FREE_OLDEST
|
||||
#define IP_REASS_FREE_OLDEST 1
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
|
||||
#define IP_REASS_FLAG_LASTFRAG 0x01
|
||||
|
||||
/** This is a helper struct which holds the starting
|
||||
* offset and the ending offset of this fragment to
|
||||
* easily chain the fragments.
|
||||
* It has the same packing requirements as the IP header, since it replaces
|
||||
* the IP header in memory in incoming fragments (after copying it) to keep
|
||||
* track of the various fragments. (-> If the IP header doesn't need packing,
|
||||
* this struct doesn't need packing, too.)
|
||||
*/
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct ip_reass_helper {
|
||||
PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
|
||||
PACK_STRUCT_FIELD(u16_t start);
|
||||
PACK_STRUCT_FIELD(u16_t end);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
|
||||
(ip4_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
|
||||
ip4_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
|
||||
IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
|
||||
|
||||
/* global variables */
|
||||
static struct ip_reassdata *reassdatagrams;
|
||||
static u16_t ip_reass_pbufcount;
|
||||
|
||||
/* function prototypes */
|
||||
static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
|
||||
static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
|
||||
|
||||
/**
|
||||
* Reassembly timer base function
|
||||
* for both NO_SYS == 0 and 1 (!).
|
||||
*
|
||||
* Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
|
||||
*/
|
||||
void
|
||||
ip_reass_tmr(void)
|
||||
{
|
||||
struct ip_reassdata *r, *prev = NULL;
|
||||
|
||||
r = reassdatagrams;
|
||||
while (r != NULL) {
|
||||
/* Decrement the timer. Once it reaches 0,
|
||||
* clean up the incomplete fragment assembly */
|
||||
if (r->timer > 0) {
|
||||
r->timer--;
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
|
||||
prev = r;
|
||||
r = r->next;
|
||||
} else {
|
||||
/* reassembly timed out */
|
||||
struct ip_reassdata *tmp;
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
|
||||
tmp = r;
|
||||
/* get the next pointer before freeing */
|
||||
r = r->next;
|
||||
/* free the helper struct and all enqueued pbufs */
|
||||
ip_reass_free_complete_datagram(tmp, prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a datagram (struct ip_reassdata) and all its pbufs.
|
||||
* Updates the total count of enqueued pbufs (ip_reass_pbufcount),
|
||||
* SNMP counters and sends an ICMP time exceeded packet.
|
||||
*
|
||||
* @param ipr datagram to free
|
||||
* @param prev the previous datagram in the linked list
|
||||
* @return the number of pbufs freed
|
||||
*/
|
||||
static int
|
||||
ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
|
||||
{
|
||||
u16_t pbufs_freed = 0;
|
||||
u16_t clen;
|
||||
struct pbuf *p;
|
||||
struct ip_reass_helper *iprh;
|
||||
|
||||
LWIP_ASSERT("prev != ipr", prev != ipr);
|
||||
if (prev != NULL) {
|
||||
LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
|
||||
}
|
||||
|
||||
MIB2_STATS_INC(mib2.ipreasmfails);
|
||||
#if LWIP_ICMP
|
||||
iprh = (struct ip_reass_helper *)ipr->p->payload;
|
||||
if (iprh->start == 0) {
|
||||
/* The first fragment was received, send ICMP time exceeded. */
|
||||
/* First, de-queue the first pbuf from r->p. */
|
||||
p = ipr->p;
|
||||
ipr->p = iprh->next_pbuf;
|
||||
/* Then, copy the original header into it. */
|
||||
SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
|
||||
icmp_time_exceeded(p, ICMP_TE_FRAG);
|
||||
clen = pbuf_clen(p);
|
||||
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
|
||||
pbufs_freed += clen;
|
||||
pbuf_free(p);
|
||||
}
|
||||
#endif /* LWIP_ICMP */
|
||||
|
||||
/* First, free all received pbufs. The individual pbufs need to be released
|
||||
separately as they have not yet been chained */
|
||||
p = ipr->p;
|
||||
while (p != NULL) {
|
||||
struct pbuf *pcur;
|
||||
iprh = (struct ip_reass_helper *)p->payload;
|
||||
pcur = p;
|
||||
/* get the next pointer before freeing */
|
||||
p = iprh->next_pbuf;
|
||||
clen = pbuf_clen(pcur);
|
||||
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
|
||||
pbufs_freed += clen;
|
||||
pbuf_free(pcur);
|
||||
}
|
||||
/* Then, unchain the struct ip_reassdata from the list and free it. */
|
||||
ip_reass_dequeue_datagram(ipr, prev);
|
||||
LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
|
||||
ip_reass_pbufcount -= pbufs_freed;
|
||||
|
||||
return pbufs_freed;
|
||||
}
|
||||
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
/**
|
||||
* Free the oldest datagram to make room for enqueueing new fragments.
|
||||
* The datagram 'fraghdr' belongs to is not freed!
|
||||
*
|
||||
* @param fraghdr IP header of the current fragment
|
||||
* @param pbufs_needed number of pbufs needed to enqueue
|
||||
* (used for freeing other datagrams if not enough space)
|
||||
* @return the number of pbufs freed
|
||||
*/
|
||||
static int
|
||||
ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
|
||||
{
|
||||
/* @todo Can't we simply remove the last datagram in the
|
||||
* linked list behind reassdatagrams?
|
||||
*/
|
||||
struct ip_reassdata *r, *oldest, *prev, *oldest_prev;
|
||||
int pbufs_freed = 0, pbufs_freed_current;
|
||||
int other_datagrams;
|
||||
|
||||
/* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
|
||||
* but don't free the datagram that 'fraghdr' belongs to! */
|
||||
do {
|
||||
oldest = NULL;
|
||||
prev = NULL;
|
||||
oldest_prev = NULL;
|
||||
other_datagrams = 0;
|
||||
r = reassdatagrams;
|
||||
while (r != NULL) {
|
||||
if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
|
||||
/* Not the same datagram as fraghdr */
|
||||
other_datagrams++;
|
||||
if (oldest == NULL) {
|
||||
oldest = r;
|
||||
oldest_prev = prev;
|
||||
} else if (r->timer <= oldest->timer) {
|
||||
/* older than the previous oldest */
|
||||
oldest = r;
|
||||
oldest_prev = prev;
|
||||
}
|
||||
}
|
||||
if (r->next != NULL) {
|
||||
prev = r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
if (oldest != NULL) {
|
||||
pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev);
|
||||
pbufs_freed += pbufs_freed_current;
|
||||
}
|
||||
} while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
|
||||
return pbufs_freed;
|
||||
}
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
|
||||
/**
|
||||
* Enqueues a new fragment into the fragment queue
|
||||
* @param fraghdr points to the new fragments IP hdr
|
||||
* @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
|
||||
* @return A pointer to the queue location into which the fragment was enqueued
|
||||
*/
|
||||
static struct ip_reassdata*
|
||||
ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
|
||||
{
|
||||
struct ip_reassdata* ipr;
|
||||
#if ! IP_REASS_FREE_OLDEST
|
||||
LWIP_UNUSED_ARG(clen);
|
||||
#endif
|
||||
|
||||
/* No matching previous fragment found, allocate a new reassdata struct */
|
||||
ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
|
||||
if (ipr == NULL) {
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
|
||||
ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
|
||||
}
|
||||
if (ipr == NULL)
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
{
|
||||
IPFRAG_STATS_INC(ip_frag.memerr);
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
memset(ipr, 0, sizeof(struct ip_reassdata));
|
||||
ipr->timer = IP_REASS_MAXAGE;
|
||||
|
||||
/* enqueue the new structure to the front of the list */
|
||||
ipr->next = reassdatagrams;
|
||||
reassdatagrams = ipr;
|
||||
/* copy the ip header for later tests and input */
|
||||
/* @todo: no ip options supported? */
|
||||
SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
|
||||
return ipr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
|
||||
* @param ipr points to the queue entry to dequeue
|
||||
*/
|
||||
static void
|
||||
ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
|
||||
{
|
||||
/* dequeue the reass struct */
|
||||
if (reassdatagrams == ipr) {
|
||||
/* it was the first in the list */
|
||||
reassdatagrams = ipr->next;
|
||||
} else {
|
||||
/* it wasn't the first, so it must have a valid 'prev' */
|
||||
LWIP_ASSERT("sanity check linked list", prev != NULL);
|
||||
prev->next = ipr->next;
|
||||
}
|
||||
|
||||
/* now we can free the ip_reassdata struct */
|
||||
memp_free(MEMP_REASSDATA, ipr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list
|
||||
* will grow over time as new pbufs are rx.
|
||||
* Also checks that the datagram passes basic continuity checks (if the last
|
||||
* fragment was received at least once).
|
||||
* @param ipr points to the reassembly state
|
||||
* @param new_p points to the pbuf for the current fragment
|
||||
* @return 0 if invalid, >0 otherwise
|
||||
*/
|
||||
static int
|
||||
ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
|
||||
{
|
||||
struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
|
||||
struct pbuf *q;
|
||||
u16_t offset, len;
|
||||
struct ip_hdr *fraghdr;
|
||||
int valid = 1;
|
||||
|
||||
/* Extract length and fragment offset from current fragment */
|
||||
fraghdr = (struct ip_hdr*)new_p->payload;
|
||||
len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
|
||||
offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
|
||||
|
||||
/* overwrite the fragment's ip header from the pbuf with our helper struct,
|
||||
* and setup the embedded helper structure. */
|
||||
/* make sure the struct ip_reass_helper fits into the IP header */
|
||||
LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
|
||||
sizeof(struct ip_reass_helper) <= IP_HLEN);
|
||||
iprh = (struct ip_reass_helper*)new_p->payload;
|
||||
iprh->next_pbuf = NULL;
|
||||
iprh->start = offset;
|
||||
iprh->end = offset + len;
|
||||
|
||||
/* Iterate through until we either get to the end of the list (append),
|
||||
* or we find one with a larger offset (insert). */
|
||||
for (q = ipr->p; q != NULL;) {
|
||||
iprh_tmp = (struct ip_reass_helper*)q->payload;
|
||||
if (iprh->start < iprh_tmp->start) {
|
||||
/* the new pbuf should be inserted before this */
|
||||
iprh->next_pbuf = q;
|
||||
if (iprh_prev != NULL) {
|
||||
/* not the fragment with the lowest offset */
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
|
||||
/* fragment overlaps with previous or following, throw away */
|
||||
goto freepbuf;
|
||||
}
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
iprh_prev->next_pbuf = new_p;
|
||||
} else {
|
||||
/* fragment with the lowest offset */
|
||||
ipr->p = new_p;
|
||||
}
|
||||
break;
|
||||
} else if (iprh->start == iprh_tmp->start) {
|
||||
/* received the same datagram twice: no need to keep the datagram */
|
||||
goto freepbuf;
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
} else if (iprh->start < iprh_tmp->end) {
|
||||
/* overlap: no need to keep the new datagram */
|
||||
goto freepbuf;
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
} else {
|
||||
/* Check if the fragments received so far have no holes. */
|
||||
if (iprh_prev != NULL) {
|
||||
if (iprh_prev->end != iprh_tmp->start) {
|
||||
/* There is a fragment missing between the current
|
||||
* and the previous fragment */
|
||||
valid = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
q = iprh_tmp->next_pbuf;
|
||||
iprh_prev = iprh_tmp;
|
||||
}
|
||||
|
||||
/* If q is NULL, then we made it to the end of the list. Determine what to do now */
|
||||
if (q == NULL) {
|
||||
if (iprh_prev != NULL) {
|
||||
/* this is (for now), the fragment with the highest offset:
|
||||
* chain it to the last fragment */
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
iprh_prev->next_pbuf = new_p;
|
||||
if (iprh_prev->end != iprh->start) {
|
||||
valid = 0;
|
||||
}
|
||||
} else {
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
LWIP_ASSERT("no previous fragment, this must be the first fragment!",
|
||||
ipr->p == NULL);
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
/* this is the first fragment we ever received for this ip datagram */
|
||||
ipr->p = new_p;
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point, the validation part begins: */
|
||||
/* If we already received the last fragment */
|
||||
if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
|
||||
/* and had no holes so far */
|
||||
if (valid) {
|
||||
/* then check if the rest of the fragments is here */
|
||||
/* Check if the queue starts with the first datagram */
|
||||
if ((ipr->p == NULL) || (((struct ip_reass_helper*)ipr->p->payload)->start != 0)) {
|
||||
valid = 0;
|
||||
} else {
|
||||
/* and check that there are no holes after this datagram */
|
||||
iprh_prev = iprh;
|
||||
q = iprh->next_pbuf;
|
||||
while (q != NULL) {
|
||||
iprh = (struct ip_reass_helper*)q->payload;
|
||||
if (iprh_prev->end != iprh->start) {
|
||||
valid = 0;
|
||||
break;
|
||||
}
|
||||
iprh_prev = iprh;
|
||||
q = iprh->next_pbuf;
|
||||
}
|
||||
/* if still valid, all fragments are received
|
||||
* (because to the MF==0 already arrived */
|
||||
if (valid) {
|
||||
LWIP_ASSERT("sanity check", ipr->p != NULL);
|
||||
LWIP_ASSERT("sanity check",
|
||||
((struct ip_reass_helper*)ipr->p->payload) != iprh);
|
||||
LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
|
||||
iprh->next_pbuf == NULL);
|
||||
LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
|
||||
iprh->end == ipr->datagram_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If valid is 0 here, there are some fragments missing in the middle
|
||||
* (since MF == 0 has already arrived). Such datagrams simply time out if
|
||||
* no more fragments are received... */
|
||||
return valid;
|
||||
}
|
||||
/* If we come here, not all fragments were received, yet! */
|
||||
return 0; /* not yet valid! */
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
freepbuf:
|
||||
ip_reass_pbufcount -= pbuf_clen(new_p);
|
||||
pbuf_free(new_p);
|
||||
return 0;
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
}
|
||||
|
||||
/**
|
||||
* Reassembles incoming IP fragments into an IP datagram.
|
||||
*
|
||||
* @param p points to a pbuf chain of the fragment
|
||||
* @return NULL if reassembly is incomplete, ? otherwise
|
||||
*/
|
||||
struct pbuf *
|
||||
ip4_reass(struct pbuf *p)
|
||||
{
|
||||
struct pbuf *r;
|
||||
struct ip_hdr *fraghdr;
|
||||
struct ip_reassdata *ipr;
|
||||
struct ip_reass_helper *iprh;
|
||||
u16_t offset, len, clen;
|
||||
|
||||
IPFRAG_STATS_INC(ip_frag.recv);
|
||||
MIB2_STATS_INC(mib2.ipreasmreqds);
|
||||
|
||||
fraghdr = (struct ip_hdr*)p->payload;
|
||||
|
||||
if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: IP options currently not supported!\n"));
|
||||
IPFRAG_STATS_INC(ip_frag.err);
|
||||
goto nullreturn;
|
||||
}
|
||||
|
||||
offset = (lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
|
||||
len = lwip_ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
|
||||
|
||||
/* Check if we are allowed to enqueue more datagrams. */
|
||||
clen = pbuf_clen(p);
|
||||
if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
|
||||
((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
{
|
||||
/* No datagram could be freed and still too many pbufs enqueued */
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
|
||||
ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
|
||||
IPFRAG_STATS_INC(ip_frag.memerr);
|
||||
/* @todo: send ICMP time exceeded here? */
|
||||
/* drop this pbuf */
|
||||
goto nullreturn;
|
||||
}
|
||||
}
|
||||
|
||||
/* Look for the datagram the fragment belongs to in the current datagram queue,
|
||||
* remembering the previous in the queue for later dequeueing. */
|
||||
for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
|
||||
/* Check if the incoming fragment matches the one currently present
|
||||
in the reassembly buffer. If so, we proceed with copying the
|
||||
fragment into the buffer. */
|
||||
if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: matching previous fragment ID=%"X16_F"\n",
|
||||
lwip_ntohs(IPH_ID(fraghdr))));
|
||||
IPFRAG_STATS_INC(ip_frag.cachehit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ipr == NULL) {
|
||||
/* Enqueue a new datagram into the datagram queue */
|
||||
ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
|
||||
/* Bail if unable to enqueue */
|
||||
if (ipr == NULL) {
|
||||
goto nullreturn;
|
||||
}
|
||||
} else {
|
||||
if (((lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
|
||||
((lwip_ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
|
||||
/* ipr->iphdr is not the header from the first fragment, but fraghdr is
|
||||
* -> copy fraghdr into ipr->iphdr since we want to have the header
|
||||
* of the first fragment (for ICMP time exceeded and later, for copying
|
||||
* all options, if supported)*/
|
||||
SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
|
||||
}
|
||||
}
|
||||
/* Track the current number of pbufs current 'in-flight', in order to limit
|
||||
the number of fragments that may be enqueued at any one time */
|
||||
ip_reass_pbufcount += clen;
|
||||
|
||||
/* At this point, we have either created a new entry or pointing
|
||||
* to an existing one */
|
||||
|
||||
/* check for 'no more fragments', and update queue entry*/
|
||||
if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
|
||||
ipr->flags |= IP_REASS_FLAG_LASTFRAG;
|
||||
ipr->datagram_len = offset + len;
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,
|
||||
("ip4_reass: last fragment seen, total len %"S16_F"\n",
|
||||
ipr->datagram_len));
|
||||
}
|
||||
/* find the right place to insert this pbuf */
|
||||
/* @todo: trim pbufs if fragments are overlapping */
|
||||
if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
|
||||
struct ip_reassdata *ipr_prev;
|
||||
/* the totally last fragment (flag more fragments = 0) was received at least
|
||||
* once AND all fragments are received */
|
||||
ipr->datagram_len += IP_HLEN;
|
||||
|
||||
/* save the second pbuf before copying the header over the pointer */
|
||||
r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
|
||||
|
||||
/* copy the original ip header back to the first pbuf */
|
||||
fraghdr = (struct ip_hdr*)(ipr->p->payload);
|
||||
SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
|
||||
IPH_LEN_SET(fraghdr, lwip_htons(ipr->datagram_len));
|
||||
IPH_OFFSET_SET(fraghdr, 0);
|
||||
IPH_CHKSUM_SET(fraghdr, 0);
|
||||
/* @todo: do we need to set/calculate the correct checksum? */
|
||||
#if CHECKSUM_GEN_IP
|
||||
IF__NETIF_CHECKSUM_ENABLED(ip_current_input_netif(), NETIF_CHECKSUM_GEN_IP) {
|
||||
IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_IP */
|
||||
|
||||
p = ipr->p;
|
||||
|
||||
/* chain together the pbufs contained within the reass_data list. */
|
||||
while (r != NULL) {
|
||||
iprh = (struct ip_reass_helper*)r->payload;
|
||||
|
||||
/* hide the ip header for every succeeding fragment */
|
||||
pbuf_header(r, -IP_HLEN);
|
||||
pbuf_cat(p, r);
|
||||
r = iprh->next_pbuf;
|
||||
}
|
||||
|
||||
/* find the previous entry in the linked list */
|
||||
if (ipr == reassdatagrams) {
|
||||
ipr_prev = NULL;
|
||||
} else {
|
||||
for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
|
||||
if (ipr_prev->next == ipr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* release the sources allocate for the fragment queue entry */
|
||||
ip_reass_dequeue_datagram(ipr, ipr_prev);
|
||||
|
||||
/* and adjust the number of pbufs currently queued for reassembly. */
|
||||
ip_reass_pbufcount -= pbuf_clen(p);
|
||||
|
||||
MIB2_STATS_INC(mib2.ipreasmoks);
|
||||
|
||||
/* Return the pbuf chain */
|
||||
return p;
|
||||
}
|
||||
/* the datagram is not (yet?) reassembled completely */
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
|
||||
return NULL;
|
||||
|
||||
nullreturn:
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: nullreturn\n"));
|
||||
IPFRAG_STATS_INC(ip_frag.drop);
|
||||
pbuf_free(p);
|
||||
return NULL;
|
||||
}
|
||||
#endif /* IP_REASSEMBLY */
|
||||
|
||||
#if IP_FRAG
|
||||
#if !LWIP_NETIF_TX_SINGLE_PBUF
|
||||
/** Allocate a new struct pbuf_custom_ref */
|
||||
static struct pbuf_custom_ref*
|
||||
ip_frag_alloc_pbuf_custom_ref(void)
|
||||
{
|
||||
return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
|
||||
}
|
||||
|
||||
/** Free a struct pbuf_custom_ref */
|
||||
static void
|
||||
ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
|
||||
{
|
||||
LWIP_ASSERT("p != NULL", p != NULL);
|
||||
memp_free(MEMP_FRAG_PBUF, p);
|
||||
}
|
||||
|
||||
/** Free-callback function to free a 'struct pbuf_custom_ref', called by
|
||||
* pbuf_free. */
|
||||
static void
|
||||
ipfrag_free_pbuf_custom(struct pbuf *p)
|
||||
{
|
||||
struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
|
||||
LWIP_ASSERT("pcr != NULL", pcr != NULL);
|
||||
LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
|
||||
if (pcr->original != NULL) {
|
||||
pbuf_free(pcr->original);
|
||||
}
|
||||
ip_frag_free_pbuf_custom_ref(pcr);
|
||||
}
|
||||
#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
|
||||
|
||||
/**
|
||||
* Fragment an IP datagram if too large for the netif.
|
||||
*
|
||||
* Chop the datagram in MTU sized chunks and send them in order
|
||||
* by pointing PBUF_REFs into p.
|
||||
*
|
||||
* @param p ip packet to send
|
||||
* @param netif the netif on which to send
|
||||
* @param dest destination ip address to which to send
|
||||
*
|
||||
* @return ERR_OK if sent successfully, err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
|
||||
{
|
||||
struct pbuf *rambuf;
|
||||
#if !LWIP_NETIF_TX_SINGLE_PBUF
|
||||
struct pbuf *newpbuf;
|
||||
#endif
|
||||
struct ip_hdr *original_iphdr;
|
||||
struct ip_hdr *iphdr;
|
||||
const u16_t nfb = (netif->mtu - IP_HLEN) / 8;
|
||||
u16_t left, fragsize;
|
||||
u16_t ofo;
|
||||
int last;
|
||||
u16_t poff = IP_HLEN;
|
||||
u16_t tmp;
|
||||
#if !LWIP_NETIF_TX_SINGLE_PBUF
|
||||
u16_t newpbuflen = 0;
|
||||
u16_t left_to_copy;
|
||||
#endif
|
||||
|
||||
original_iphdr = (struct ip_hdr *)p->payload;
|
||||
iphdr = original_iphdr;
|
||||
LWIP_ERROR("ip4_frag() does not support IP options", IPH_HL(iphdr) * 4 == IP_HLEN, return ERR_VAL);
|
||||
|
||||
/* Save original offset */
|
||||
tmp = lwip_ntohs(IPH_OFFSET(iphdr));
|
||||
ofo = tmp & IP_OFFMASK;
|
||||
LWIP_ERROR("ip_frag(): MF already set", (tmp & IP_MF) == 0, return ERR_VAL);
|
||||
|
||||
left = p->tot_len - IP_HLEN;
|
||||
|
||||
while (left) {
|
||||
/* Fill this fragment */
|
||||
fragsize = LWIP_MIN(left, nfb * 8);
|
||||
|
||||
#if LWIP_NETIF_TX_SINGLE_PBUF
|
||||
rambuf = pbuf_alloc(PBUF_IP, fragsize, PBUF_RAM);
|
||||
if (rambuf == NULL) {
|
||||
goto memerr;
|
||||
}
|
||||
LWIP_ASSERT("this needs a pbuf in one piece!",
|
||||
(rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
|
||||
poff += pbuf_copy_partial(p, rambuf->payload, fragsize, poff);
|
||||
/* make room for the IP header */
|
||||
if (pbuf_header(rambuf, IP_HLEN)) {
|
||||
pbuf_free(rambuf);
|
||||
goto memerr;
|
||||
}
|
||||
/* fill in the IP header */
|
||||
SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
|
||||
iphdr = (struct ip_hdr*)rambuf->payload;
|
||||
#else /* LWIP_NETIF_TX_SINGLE_PBUF */
|
||||
/* When not using a static buffer, create a chain of pbufs.
|
||||
* The first will be a PBUF_RAM holding the link and IP header.
|
||||
* The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
|
||||
* but limited to the size of an mtu.
|
||||
*/
|
||||
rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
|
||||
if (rambuf == NULL) {
|
||||
goto memerr;
|
||||
}
|
||||
LWIP_ASSERT("this needs a pbuf in one piece!",
|
||||
(p->len >= (IP_HLEN)));
|
||||
SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
|
||||
iphdr = (struct ip_hdr *)rambuf->payload;
|
||||
|
||||
left_to_copy = fragsize;
|
||||
while (left_to_copy) {
|
||||
struct pbuf_custom_ref *pcr;
|
||||
u16_t plen = p->len - poff;
|
||||
newpbuflen = LWIP_MIN(left_to_copy, plen);
|
||||
/* Is this pbuf already empty? */
|
||||
if (!newpbuflen) {
|
||||
poff = 0;
|
||||
p = p->next;
|
||||
continue;
|
||||
}
|
||||
pcr = ip_frag_alloc_pbuf_custom_ref();
|
||||
if (pcr == NULL) {
|
||||
pbuf_free(rambuf);
|
||||
goto memerr;
|
||||
}
|
||||
/* Mirror this pbuf, although we might not need all of it. */
|
||||
newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc,
|
||||
(u8_t*)p->payload + poff, newpbuflen);
|
||||
if (newpbuf == NULL) {
|
||||
ip_frag_free_pbuf_custom_ref(pcr);
|
||||
pbuf_free(rambuf);
|
||||
goto memerr;
|
||||
}
|
||||
pbuf_ref(p);
|
||||
pcr->original = p;
|
||||
pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
|
||||
|
||||
/* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
|
||||
* so that it is removed when pbuf_dechain is later called on rambuf.
|
||||
*/
|
||||
pbuf_cat(rambuf, newpbuf);
|
||||
left_to_copy -= newpbuflen;
|
||||
if (left_to_copy) {
|
||||
poff = 0;
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
poff += newpbuflen;
|
||||
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
|
||||
|
||||
/* Correct header */
|
||||
last = (left <= netif->mtu - IP_HLEN);
|
||||
|
||||
/* Set new offset and MF flag */
|
||||
tmp = (IP_OFFMASK & (ofo));
|
||||
if (!last) {
|
||||
tmp = tmp | IP_MF;
|
||||
}
|
||||
IPH_OFFSET_SET(iphdr, lwip_htons(tmp));
|
||||
IPH_LEN_SET(iphdr, lwip_htons(fragsize + IP_HLEN));
|
||||
IPH_CHKSUM_SET(iphdr, 0);
|
||||
#if CHECKSUM_GEN_IP
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
|
||||
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_IP */
|
||||
|
||||
/* No need for separate header pbuf - we allowed room for it in rambuf
|
||||
* when allocated.
|
||||
*/
|
||||
netif->output(netif, rambuf, dest);
|
||||
IPFRAG_STATS_INC(ip_frag.xmit);
|
||||
|
||||
/* Unfortunately we can't reuse rambuf - the hardware may still be
|
||||
* using the buffer. Instead we free it (and the ensuing chain) and
|
||||
* recreate it next time round the loop. If we're lucky the hardware
|
||||
* will have already sent the packet, the free will really free, and
|
||||
* there will be zero memory penalty.
|
||||
*/
|
||||
|
||||
pbuf_free(rambuf);
|
||||
left -= fragsize;
|
||||
ofo += nfb;
|
||||
}
|
||||
MIB2_STATS_INC(mib2.ipfragoks);
|
||||
return ERR_OK;
|
||||
memerr:
|
||||
MIB2_STATS_INC(mib2.ipfragfails);
|
||||
return ERR_MEM;
|
||||
}
|
||||
#endif /* IP_FRAG */
|
||||
|
||||
#endif /* LWIP_IPV4 */
|
||||
@@ -1,359 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Jani Monoses <jani@iv.ro>
|
||||
* original reassembly code by Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* ip_frag.c
|
||||
*
|
||||
* This is the code for IP segmentation and reassembly
|
||||
*
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/sys.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/ip_frag.h"
|
||||
#include "lwip/netif.h"
|
||||
|
||||
#include "lwip/stats.h"
|
||||
|
||||
|
||||
/*
|
||||
* Copy len bytes from offset in pbuf to buffer
|
||||
*
|
||||
* helper used by both ip_reass and ip_frag
|
||||
*/
|
||||
static struct pbuf *
|
||||
copy_from_pbuf(struct pbuf *p, u16_t * offset,
|
||||
u8_t * buffer, u16_t len)
|
||||
{
|
||||
u16_t l;
|
||||
|
||||
p->payload = (u8_t *)p->payload + *offset;
|
||||
p->len -= *offset;
|
||||
while (len) {
|
||||
l = len < p->len ? len : p->len;
|
||||
memcpy(buffer, p->payload, l);
|
||||
buffer += l;
|
||||
len -= l;
|
||||
if (len)
|
||||
p = p->next;
|
||||
else
|
||||
*offset = l;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#define IP_REASS_BUFSIZE 5760
|
||||
#define IP_REASS_MAXAGE 30
|
||||
#define IP_REASS_TMO 1000
|
||||
|
||||
static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE];
|
||||
static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8)];
|
||||
static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f,
|
||||
0x0f, 0x07, 0x03, 0x01
|
||||
};
|
||||
static u16_t ip_reasslen;
|
||||
static u8_t ip_reassflags;
|
||||
#define IP_REASS_FLAG_LASTFRAG 0x01
|
||||
|
||||
static u8_t ip_reasstmr;
|
||||
|
||||
/* Reassembly timer */
|
||||
static void
|
||||
ip_reass_timer(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
if (ip_reasstmr > 1) {
|
||||
ip_reasstmr--;
|
||||
sys_timeout(IP_REASS_TMO, ip_reass_timer, NULL);
|
||||
} else if (ip_reasstmr == 1)
|
||||
ip_reasstmr = 0;
|
||||
}
|
||||
|
||||
struct pbuf *
|
||||
ip_reass(struct pbuf *p)
|
||||
{
|
||||
struct pbuf *q;
|
||||
struct ip_hdr *fraghdr, *iphdr;
|
||||
u16_t offset, len;
|
||||
u16_t i;
|
||||
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip_frag.recv;
|
||||
#endif /* IP_STATS */
|
||||
|
||||
iphdr = (struct ip_hdr *) ip_reassbuf;
|
||||
fraghdr = (struct ip_hdr *) p->payload;
|
||||
/* If ip_reasstmr is zero, no packet is present in the buffer, so we
|
||||
write the IP header of the fragment into the reassembly
|
||||
buffer. The timer is updated with the maximum age. */
|
||||
if (ip_reasstmr == 0) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n"));
|
||||
memcpy(iphdr, fraghdr, IP_HLEN);
|
||||
ip_reasstmr = IP_REASS_MAXAGE;
|
||||
sys_timeout(IP_REASS_TMO, ip_reass_timer, NULL);
|
||||
ip_reassflags = 0;
|
||||
/* Clear the bitmap. */
|
||||
memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));
|
||||
}
|
||||
|
||||
/* Check if the incoming fragment matches the one currently present
|
||||
in the reasembly buffer. If so, we proceed with copying the
|
||||
fragment into the buffer. */
|
||||
if (ip_addr_cmp(&iphdr->src, &fraghdr->src) &&
|
||||
ip_addr_cmp(&iphdr->dest, &fraghdr->dest) &&
|
||||
IPH_ID(iphdr) == IPH_ID(fraghdr)) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching old packet\n"));
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip_frag.cachehit;
|
||||
#endif /* IP_STATS */
|
||||
/* Find out the offset in the reassembly buffer where we should
|
||||
copy the fragment. */
|
||||
len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
|
||||
offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
|
||||
|
||||
/* If the offset or the offset + fragment length overflows the
|
||||
reassembly buffer, we discard the entire packet. */
|
||||
if (offset > IP_REASS_BUFSIZE || offset + len > IP_REASS_BUFSIZE) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,
|
||||
("ip_reass: fragment outside of buffer (%d:%d/%d).\n", offset,
|
||||
offset + len, IP_REASS_BUFSIZE));
|
||||
sys_untimeout(ip_reass_timer, NULL);
|
||||
ip_reasstmr = 0;
|
||||
goto nullreturn;
|
||||
}
|
||||
|
||||
/* Copy the fragment into the reassembly buffer, at the right
|
||||
offset. */
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,
|
||||
("ip_reass: copying with offset %d into %d:%d\n", offset,
|
||||
IP_HLEN + offset, IP_HLEN + offset + len));
|
||||
i = IPH_HL(fraghdr) * 4;
|
||||
copy_from_pbuf(p, &i, &ip_reassbuf[IP_HLEN + offset], len);
|
||||
|
||||
/* Update the bitmap. */
|
||||
if (offset / (8 * 8) == (offset + len) / (8 * 8)) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,
|
||||
("ip_reass: updating single byte in bitmap.\n"));
|
||||
/* If the two endpoints are in the same byte, we only update
|
||||
that byte. */
|
||||
ip_reassbitmap[offset / (8 * 8)] |=
|
||||
bitmap_bits[(offset / 8) & 7] &
|
||||
~bitmap_bits[((offset + len) / 8) & 7];
|
||||
} else {
|
||||
/* If the two endpoints are in different bytes, we update the
|
||||
bytes in the endpoints and fill the stuff inbetween with
|
||||
0xff. */
|
||||
ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8) & 7];
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,
|
||||
("ip_reass: updating many bytes in bitmap (%d:%d).\n",
|
||||
1 + offset / (8 * 8), (offset + len) / (8 * 8)));
|
||||
for (i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {
|
||||
ip_reassbitmap[i] = 0xff;
|
||||
}
|
||||
ip_reassbitmap[(offset + len) / (8 * 8)] |=
|
||||
~bitmap_bits[((offset + len) / 8) & 7];
|
||||
}
|
||||
|
||||
/* If this fragment has the More Fragments flag set to zero, we
|
||||
know that this is the last fragment, so we can calculate the
|
||||
size of the entire packet. We also set the
|
||||
IP_REASS_FLAG_LASTFRAG flag to indicate that we have received
|
||||
the final fragment. */
|
||||
|
||||
if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
|
||||
ip_reassflags |= IP_REASS_FLAG_LASTFRAG;
|
||||
ip_reasslen = offset + len;
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,
|
||||
("ip_reass: last fragment seen, total len %d\n",
|
||||
ip_reasslen));
|
||||
}
|
||||
|
||||
/* Finally, we check if we have a full packet in the buffer. We do
|
||||
this by checking if we have the last fragment and if all bits
|
||||
in the bitmap are set. */
|
||||
if (ip_reassflags & IP_REASS_FLAG_LASTFRAG) {
|
||||
/* Check all bytes up to and including all but the last byte in
|
||||
the bitmap. */
|
||||
for (i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) {
|
||||
if (ip_reassbitmap[i] != 0xff) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,
|
||||
("ip_reass: last fragment seen, bitmap %d/%d failed (%x)\n",
|
||||
i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i]));
|
||||
goto nullreturn;
|
||||
}
|
||||
}
|
||||
/* Check the last byte in the bitmap. It should contain just the
|
||||
right amount of bits. */
|
||||
if (ip_reassbitmap[ip_reasslen / (8 * 8)] !=
|
||||
(u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) {
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,
|
||||
("ip_reass: last fragment seen, bitmap %d didn't contain %x (%x)\n",
|
||||
ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7],
|
||||
ip_reassbitmap[ip_reasslen / (8 * 8)]));
|
||||
goto nullreturn;
|
||||
}
|
||||
|
||||
/* Pretend to be a "normal" (i.e., not fragmented) IP packet
|
||||
from now on. */
|
||||
ip_reasslen += IP_HLEN;
|
||||
|
||||
IPH_LEN_SET(iphdr, htons(ip_reasslen));
|
||||
IPH_OFFSET_SET(iphdr, 0);
|
||||
IPH_CHKSUM_SET(iphdr, 0);
|
||||
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
|
||||
|
||||
/* If we have come this far, we have a full packet in the
|
||||
buffer, so we allocate a pbuf and copy the packet into it. We
|
||||
also reset the timer. */
|
||||
sys_untimeout(ip_reass_timer, NULL);
|
||||
ip_reasstmr = 0;
|
||||
pbuf_free(p);
|
||||
p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);
|
||||
if (p != NULL) {
|
||||
i = 0;
|
||||
for (q = p; q != NULL; q = q->next) {
|
||||
/* Copy enough bytes to fill this pbuf in the chain. The
|
||||
available data in the pbuf is given by the q->len
|
||||
variable. */
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG,
|
||||
("ip_reass: memcpy from %p (%d) to %p, %d bytes\n",
|
||||
&ip_reassbuf[i], i, q->payload,
|
||||
q->len > ip_reasslen - i ? ip_reasslen - i : q->len));
|
||||
memcpy(q->payload, &ip_reassbuf[i],
|
||||
q->len > ip_reasslen - i ? ip_reasslen - i : q->len);
|
||||
i += q->len;
|
||||
}
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip_frag.fw;
|
||||
#endif /* IP_STATS */
|
||||
} else {
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip_frag.memerr;
|
||||
#endif /* IP_STATS */
|
||||
}
|
||||
LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
nullreturn:
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip_frag.drop;
|
||||
#endif /* IP_STATS */
|
||||
pbuf_free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define MAX_MTU 1500
|
||||
static u8_t buf[MEM_ALIGN_SIZE(MAX_MTU)];
|
||||
|
||||
/**
|
||||
* Fragment an IP packet if too large
|
||||
*
|
||||
* Chop the packet in mtu sized chunks and send them in order
|
||||
* by using a fixed size static memory buffer (PBUF_ROM)
|
||||
*/
|
||||
err_t
|
||||
ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
|
||||
{
|
||||
struct pbuf *rambuf;
|
||||
struct pbuf *header;
|
||||
struct ip_hdr *iphdr;
|
||||
u16_t nfb = 0;
|
||||
u16_t left, cop;
|
||||
u16_t mtu = netif->mtu;
|
||||
u16_t ofo, omf;
|
||||
u16_t last;
|
||||
u16_t poff = IP_HLEN;
|
||||
u16_t tmp;
|
||||
|
||||
/* Get a RAM based MTU sized pbuf */
|
||||
rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
|
||||
rambuf->tot_len = rambuf->len = mtu;
|
||||
rambuf->payload = MEM_ALIGN((void *)buf);
|
||||
|
||||
|
||||
/* Copy the IP header in it */
|
||||
iphdr = rambuf->payload;
|
||||
memcpy(iphdr, p->payload, IP_HLEN);
|
||||
|
||||
/* Save original offset */
|
||||
tmp = ntohs(IPH_OFFSET(iphdr));
|
||||
ofo = tmp & IP_OFFMASK;
|
||||
omf = tmp & IP_MF;
|
||||
|
||||
left = p->tot_len - IP_HLEN;
|
||||
|
||||
while (left) {
|
||||
last = (left <= mtu - IP_HLEN);
|
||||
|
||||
/* Set new offset and MF flag */
|
||||
ofo += nfb;
|
||||
tmp = omf | (IP_OFFMASK & (ofo));
|
||||
if (!last)
|
||||
tmp = tmp | IP_MF;
|
||||
IPH_OFFSET_SET(iphdr, htons(tmp));
|
||||
|
||||
/* Fill this fragment */
|
||||
nfb = (mtu - IP_HLEN) / 8;
|
||||
cop = last ? left : nfb * 8;
|
||||
|
||||
p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);
|
||||
|
||||
/* Correct header */
|
||||
IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
|
||||
IPH_CHKSUM_SET(iphdr, 0);
|
||||
IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
|
||||
|
||||
if (last)
|
||||
pbuf_realloc(rambuf, left + IP_HLEN);
|
||||
/* This part is ugly: we alloc a RAM based pbuf for
|
||||
* the link level header for each chunk and then
|
||||
* free it.A PBUF_ROM style pbuf for which pbuf_header
|
||||
* worked would make things simpler.
|
||||
*/
|
||||
header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
|
||||
pbuf_chain(header, rambuf);
|
||||
netif->output(netif, header, dest);
|
||||
#ifdef IP_STATS
|
||||
++lwip_stats.ip_frag.xmit;
|
||||
#endif /* IP_STATS */
|
||||
pbuf_free(header);
|
||||
|
||||
left -= cop;
|
||||
}
|
||||
pbuf_free(rambuf);
|
||||
return ERR_OK;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
IPv6 support in lwIP is very experimental.
|
||||
50
src/core/ipv6/dhcp6.c
Normal file
50
src/core/ipv6/dhcp6.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* DHCPv6.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV6 && LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/def.h"
|
||||
|
||||
|
||||
#endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */
|
||||
126
src/core/ipv6/ethip6.c
Normal file
126
src/core/ipv6/ethip6.c
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* Ethernet output for IPv6. Uses ND tables for link-layer addressing.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV6 && LWIP_ETHERNET
|
||||
|
||||
#include "lwip/ethip6.h"
|
||||
#include "lwip/nd6.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/icmp6.h"
|
||||
#include "lwip/prot/ethernet.h"
|
||||
#include "netif/ethernet.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Resolve and fill-in Ethernet address header for outgoing IPv6 packet.
|
||||
*
|
||||
* For IPv6 multicast, corresponding Ethernet addresses
|
||||
* are selected and the packet is transmitted on the link.
|
||||
*
|
||||
* For unicast addresses, ...
|
||||
*
|
||||
* @todo anycast addresses
|
||||
*
|
||||
* @param netif The lwIP network interface which the IP packet will be sent on.
|
||||
* @param q The pbuf(s) containing the IP packet to be sent.
|
||||
* @param ip6addr The IP address of the packet destination.
|
||||
*
|
||||
* @return
|
||||
* - ERR_RTE No route to destination (no gateway to external networks),
|
||||
* or the return type of either nd6_queue_packet() or ethernet_output().
|
||||
*/
|
||||
err_t
|
||||
ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
|
||||
{
|
||||
struct eth_addr dest;
|
||||
s8_t i;
|
||||
|
||||
/* multicast destination IP address? */
|
||||
if (ip6_addr_ismulticast(ip6addr)) {
|
||||
/* Hash IP multicast address to MAC address.*/
|
||||
dest.addr[0] = 0x33;
|
||||
dest.addr[1] = 0x33;
|
||||
dest.addr[2] = ((const u8_t *)(&(ip6addr->addr[3])))[0];
|
||||
dest.addr[3] = ((const u8_t *)(&(ip6addr->addr[3])))[1];
|
||||
dest.addr[4] = ((const u8_t *)(&(ip6addr->addr[3])))[2];
|
||||
dest.addr[5] = ((const u8_t *)(&(ip6addr->addr[3])))[3];
|
||||
|
||||
/* Send out. */
|
||||
return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
|
||||
}
|
||||
|
||||
/* We have a unicast destination IP address */
|
||||
/* @todo anycast? */
|
||||
/* Get next hop record. */
|
||||
i = nd6_get_next_hop_entry(ip6addr, netif);
|
||||
if (i < 0) {
|
||||
/* failed to get a next hop neighbor record. */
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
/* Now that we have a destination record, send or queue the packet. */
|
||||
if (neighbor_cache[i].state == ND6_STALE) {
|
||||
/* Switch to delay state. */
|
||||
neighbor_cache[i].state = ND6_DELAY;
|
||||
neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
|
||||
}
|
||||
/* @todo should we send or queue if PROBE? send for now, to let unicast NS pass. */
|
||||
if ((neighbor_cache[i].state == ND6_REACHABLE) ||
|
||||
(neighbor_cache[i].state == ND6_DELAY) ||
|
||||
(neighbor_cache[i].state == ND6_PROBE)) {
|
||||
|
||||
/* Send out. */
|
||||
SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6);
|
||||
return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
|
||||
}
|
||||
|
||||
/* We should queue packet on this interface. */
|
||||
return nd6_queue_packet(i, q);
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV6 && LWIP_ETHERNET */
|
||||
@@ -1,5 +1,11 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* IPv6 version of ICMP, as per RFC 4443.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
@@ -26,159 +32,319 @@
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
* Author: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
/* Some ICMP messages should be passed to the transport protocols. This
|
||||
is not implemented. */
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#include "lwip/icmp.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/def.h"
|
||||
#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/icmp6.h"
|
||||
#include "lwip/prot/icmp6.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/nd6.h"
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
#include <string.h>
|
||||
|
||||
#ifndef LWIP_ICMP6_DATASIZE
|
||||
#define LWIP_ICMP6_DATASIZE 8
|
||||
#endif
|
||||
#if LWIP_ICMP6_DATASIZE == 0
|
||||
#define LWIP_ICMP6_DATASIZE 8
|
||||
#endif
|
||||
|
||||
/* Forward declarations */
|
||||
static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
|
||||
|
||||
|
||||
/**
|
||||
* Process an input ICMPv6 message. Called by ip6_input.
|
||||
*
|
||||
* Will generate a reply for echo requests. Other messages are forwarded
|
||||
* to nd6_input, or mld6_input.
|
||||
*
|
||||
* @param p the mld packet, p->payload pointing to the icmpv6 header
|
||||
* @param inp the netif on which this packet was received
|
||||
*/
|
||||
void
|
||||
icmp_input(struct pbuf *p, struct netif *inp)
|
||||
icmp6_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
unsigned char type;
|
||||
struct icmp_echo_hdr *iecho;
|
||||
struct ip_hdr *iphdr;
|
||||
struct ip_addr tmpaddr;
|
||||
struct icmp6_hdr *icmp6hdr;
|
||||
struct pbuf *r;
|
||||
const ip6_addr_t *reply_src;
|
||||
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.recv;
|
||||
#endif /* ICMP_STATS */
|
||||
ICMP6_STATS_INC(icmp6.recv);
|
||||
|
||||
/* TODO: check length before accessing payload! */
|
||||
/* Check that ICMPv6 header fits in payload */
|
||||
if (p->len < sizeof(struct icmp6_hdr)) {
|
||||
/* drop short packets */
|
||||
pbuf_free(p);
|
||||
ICMP6_STATS_INC(icmp6.lenerr);
|
||||
ICMP6_STATS_INC(icmp6.drop);
|
||||
return;
|
||||
}
|
||||
|
||||
type = ((char *)p->payload)[0];
|
||||
|
||||
switch (type) {
|
||||
case ICMP6_ECHO:
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
|
||||
|
||||
if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
|
||||
icmp6hdr = (struct icmp6_hdr *)p->payload;
|
||||
|
||||
#if CHECKSUM_CHECK_ICMP6
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_CHECK_ICMP6) {
|
||||
if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
|
||||
ip6_current_dest_addr()) != 0) {
|
||||
/* Checksum failed */
|
||||
pbuf_free(p);
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.lenerr;
|
||||
#endif /* ICMP_STATS */
|
||||
|
||||
ICMP6_STATS_INC(icmp6.chkerr);
|
||||
ICMP6_STATS_INC(icmp6.drop);
|
||||
return;
|
||||
}
|
||||
iecho = p->payload;
|
||||
iphdr = (struct ip_hdr *)((char *)p->payload - IP_HLEN);
|
||||
if (inet_chksum_pbuf(p) != 0) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%x)\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len)));
|
||||
}
|
||||
#endif /* CHECKSUM_CHECK_ICMP6 */
|
||||
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.chkerr;
|
||||
#endif /* ICMP_STATS */
|
||||
/* return;*/
|
||||
switch (icmp6hdr->type) {
|
||||
case ICMP6_TYPE_NA: /* Neighbor advertisement */
|
||||
case ICMP6_TYPE_NS: /* Neighbor solicitation */
|
||||
case ICMP6_TYPE_RA: /* Router advertisement */
|
||||
case ICMP6_TYPE_RD: /* Redirect */
|
||||
case ICMP6_TYPE_PTB: /* Packet too big */
|
||||
nd6_input(p, inp);
|
||||
return;
|
||||
break;
|
||||
case ICMP6_TYPE_RS:
|
||||
#if LWIP_IPV6_FORWARD
|
||||
/* @todo implement router functionality */
|
||||
#endif
|
||||
break;
|
||||
#if LWIP_IPV6_MLD
|
||||
case ICMP6_TYPE_MLQ:
|
||||
case ICMP6_TYPE_MLR:
|
||||
case ICMP6_TYPE_MLD:
|
||||
mld6_input(p, inp);
|
||||
return;
|
||||
break;
|
||||
#endif
|
||||
case ICMP6_TYPE_EREQ:
|
||||
#if !LWIP_MULTICAST_PING
|
||||
/* multicast destination address? */
|
||||
if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
|
||||
/* drop */
|
||||
pbuf_free(p);
|
||||
ICMP6_STATS_INC(icmp6.drop);
|
||||
return;
|
||||
}
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp: p->len %d p->tot_len %d\n", p->len, p->tot_len));
|
||||
ip_addr_set(&tmpaddr, &(iphdr->src));
|
||||
ip_addr_set(&(iphdr->src), &(iphdr->dest));
|
||||
ip_addr_set(&(iphdr->dest), &tmpaddr);
|
||||
iecho->type = ICMP6_ER;
|
||||
/* adjust the checksum */
|
||||
if (iecho->chksum >= htons(0xffff - (ICMP6_ECHO << 8))) {
|
||||
iecho->chksum += htons(ICMP6_ECHO << 8) + 1;
|
||||
} else {
|
||||
iecho->chksum += htons(ICMP6_ECHO << 8);
|
||||
}
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo (%x)\n", inet_chksum_pseudo(p, &(iphdr->src), &(iphdr->dest), IP_PROTO_ICMP, p->tot_len)));
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.xmit;
|
||||
#endif /* ICMP_STATS */
|
||||
#endif /* LWIP_MULTICAST_PING */
|
||||
|
||||
/* Allocate reply. */
|
||||
r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
|
||||
if (r == NULL) {
|
||||
/* drop */
|
||||
pbuf_free(p);
|
||||
ICMP6_STATS_INC(icmp6.memerr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy echo request. */
|
||||
if (pbuf_copy(r, p) != ERR_OK) {
|
||||
/* drop */
|
||||
pbuf_free(p);
|
||||
pbuf_free(r);
|
||||
ICMP6_STATS_INC(icmp6.err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Determine reply source IPv6 address. */
|
||||
#if LWIP_MULTICAST_PING
|
||||
if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
|
||||
reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr()));
|
||||
if (reply_src == NULL) {
|
||||
/* drop */
|
||||
pbuf_free(p);
|
||||
pbuf_free(r);
|
||||
ICMP6_STATS_INC(icmp6.rterr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif /* LWIP_MULTICAST_PING */
|
||||
{
|
||||
reply_src = ip6_current_dest_addr();
|
||||
}
|
||||
|
||||
/* Set fields in reply. */
|
||||
((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
|
||||
((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
|
||||
#if CHECKSUM_GEN_ICMP6
|
||||
IF__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP6) {
|
||||
((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
|
||||
IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_ICMP6 */
|
||||
|
||||
/* Send reply. */
|
||||
ICMP6_STATS_INC(icmp6.xmit);
|
||||
ip6_output_if(r, reply_src, ip6_current_src_addr(),
|
||||
LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp);
|
||||
pbuf_free(r);
|
||||
|
||||
/* LWIP_DEBUGF("icmp: p->len %u p->tot_len %u\n", p->len, p->tot_len);*/
|
||||
ip_output_if (p, &(iphdr->src), IP_HDRINCL,
|
||||
iphdr->hoplim, IP_PROTO_ICMP, inp);
|
||||
break;
|
||||
default:
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %d not supported.\n", (int)type));
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.proterr;
|
||||
++lwip_stats.icmp.drop;
|
||||
#endif /* ICMP_STATS */
|
||||
ICMP6_STATS_INC(icmp6.proterr);
|
||||
ICMP6_STATS_INC(icmp6.drop);
|
||||
break;
|
||||
}
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/**
|
||||
* Send an icmpv6 'destination unreachable' packet.
|
||||
*
|
||||
* @param p the input packet for which the 'unreachable' should be sent,
|
||||
* p->payload pointing to the IPv6 header
|
||||
* @param c ICMPv6 code for the unreachable type
|
||||
*/
|
||||
void
|
||||
icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
|
||||
icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
|
||||
{
|
||||
struct pbuf *q;
|
||||
struct ip_hdr *iphdr;
|
||||
struct icmp_dur_hdr *idur;
|
||||
|
||||
q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
|
||||
/* ICMP header + IP header + 8 bytes of data */
|
||||
|
||||
iphdr = p->payload;
|
||||
|
||||
idur = q->payload;
|
||||
idur->type = (char)ICMP6_DUR;
|
||||
idur->icode = (char)t;
|
||||
|
||||
memcpy((char *)q->payload + 8, p->payload, IP_HLEN + 8);
|
||||
|
||||
/* calculate checksum */
|
||||
idur->chksum = 0;
|
||||
idur->chksum = inet_chksum(idur, q->len);
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.xmit;
|
||||
#endif /* ICMP_STATS */
|
||||
|
||||
ip_output(q, NULL,
|
||||
(struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP);
|
||||
pbuf_free(q);
|
||||
icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Send an icmpv6 'packet too big' packet.
|
||||
*
|
||||
* @param p the input packet for which the 'packet too big' should be sent,
|
||||
* p->payload pointing to the IPv6 header
|
||||
* @param mtu the maximum mtu that we can accept
|
||||
*/
|
||||
void
|
||||
icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
|
||||
icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
|
||||
{
|
||||
icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an icmpv6 'time exceeded' packet.
|
||||
*
|
||||
* @param p the input packet for which the 'unreachable' should be sent,
|
||||
* p->payload pointing to the IPv6 header
|
||||
* @param c ICMPv6 code for the time exceeded type
|
||||
*/
|
||||
void
|
||||
icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
|
||||
{
|
||||
icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an icmpv6 'parameter problem' packet.
|
||||
*
|
||||
* @param p the input packet for which the 'param problem' should be sent,
|
||||
* p->payload pointing to the IP header
|
||||
* @param c ICMPv6 code for the param problem type
|
||||
* @param pointer the pointer to the byte where the parameter is found
|
||||
*/
|
||||
void
|
||||
icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
|
||||
{
|
||||
icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ICMPv6 packet in response to an incoming packet.
|
||||
*
|
||||
* @param p the input packet for which the response should be sent,
|
||||
* p->payload pointing to the IPv6 header
|
||||
* @param code Code of the ICMPv6 header
|
||||
* @param data Additional 32-bit parameter in the ICMPv6 header
|
||||
* @param type Type of the ICMPv6 header
|
||||
*/
|
||||
static void
|
||||
icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
|
||||
{
|
||||
struct pbuf *q;
|
||||
struct ip_hdr *iphdr;
|
||||
struct icmp_te_hdr *tehdr;
|
||||
struct icmp6_hdr *icmp6hdr;
|
||||
const ip6_addr_t *reply_src;
|
||||
ip6_addr_t *reply_dest;
|
||||
ip6_addr_t reply_src_local, reply_dest_local;
|
||||
struct ip6_hdr *ip6hdr;
|
||||
struct netif *netif;
|
||||
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded\n"));
|
||||
/* ICMPv6 header + IPv6 header + data */
|
||||
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
|
||||
PBUF_RAM);
|
||||
if (q == NULL) {
|
||||
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
|
||||
ICMP6_STATS_INC(icmp6.memerr);
|
||||
return;
|
||||
}
|
||||
LWIP_ASSERT("check that first pbuf can hold icmp 6message",
|
||||
(q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
|
||||
|
||||
q = pbuf_alloc(PBUF_IP, 8 + IP_HLEN + 8, PBUF_RAM);
|
||||
|
||||
iphdr = p->payload;
|
||||
|
||||
tehdr = q->payload;
|
||||
tehdr->type = (char)ICMP6_TE;
|
||||
tehdr->icode = (char)t;
|
||||
icmp6hdr = (struct icmp6_hdr *)q->payload;
|
||||
icmp6hdr->type = type;
|
||||
icmp6hdr->code = code;
|
||||
icmp6hdr->data = data;
|
||||
|
||||
/* copy fields from original packet */
|
||||
memcpy((char *)q->payload + 8, (char *)p->payload, IP_HLEN + 8);
|
||||
SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
|
||||
IP6_HLEN + LWIP_ICMP6_DATASIZE);
|
||||
|
||||
/* Get the destination address and netif for this ICMP message. */
|
||||
if ((ip_current_netif() == NULL) ||
|
||||
((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) {
|
||||
/* Special case, as ip6_current_xxx is either NULL, or points
|
||||
* to a different packet than the one that expired.
|
||||
* We must use the addresses that are stored in the expired packet. */
|
||||
ip6hdr = (struct ip6_hdr *)p->payload;
|
||||
/* copy from packed address to aligned address */
|
||||
ip6_addr_copy(reply_dest_local, ip6hdr->src);
|
||||
ip6_addr_copy(reply_src_local, ip6hdr->dest);
|
||||
reply_dest = &reply_dest_local;
|
||||
reply_src = &reply_src_local;
|
||||
netif = ip6_route(reply_src, reply_dest);
|
||||
if (netif == NULL) {
|
||||
/* drop */
|
||||
pbuf_free(q);
|
||||
ICMP6_STATS_INC(icmp6.rterr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
netif = ip_current_netif();
|
||||
reply_dest = ip6_current_src_addr();
|
||||
|
||||
/* Select an address to use as source. */
|
||||
reply_src = ip_2_ip6(ip6_select_source_address(netif, reply_dest));
|
||||
if (reply_src == NULL) {
|
||||
/* drop */
|
||||
pbuf_free(q);
|
||||
ICMP6_STATS_INC(icmp6.rterr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* calculate checksum */
|
||||
tehdr->chksum = 0;
|
||||
tehdr->chksum = inet_chksum(tehdr, q->len);
|
||||
#ifdef ICMP_STATS
|
||||
++lwip_stats.icmp.xmit;
|
||||
#endif /* ICMP_STATS */
|
||||
ip_output(q, NULL,
|
||||
(struct ip_addr *)&(iphdr->src), ICMP_TTL, IP_PROTO_ICMP);
|
||||
icmp6hdr->chksum = 0;
|
||||
#if CHECKSUM_GEN_ICMP6
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
|
||||
icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
|
||||
reply_src, reply_dest);
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_ICMP6 */
|
||||
|
||||
ICMP6_STATS_INC(icmp6.xmit);
|
||||
ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
|
||||
pbuf_free(q);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* LWIP_ICMP6 && LWIP_IPV6 */
|
||||
|
||||
53
src/core/ipv6/inet6.c
Normal file
53
src/core/ipv6/inet6.c
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* INET v6 addresses.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/inet.h"
|
||||
|
||||
/** This variable is initialized by the system to contain the wildcard IPv6 address.
|
||||
*/
|
||||
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
|
||||
|
||||
#endif /* LWIP_IPV6 */
|
||||
1272
src/core/ipv6/ip6.c
1272
src/core/ipv6/ip6.c
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,14 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* IPv6 addresses.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
@@ -11,80 +17,276 @@
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
*
|
||||
* Author: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
* Functions for handling IPv6 addresses.
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/def.h"
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/* used by IP6_ADDR_ANY(6) in ip6_addr.h */
|
||||
const ip_addr_t ip6_addr_any = IPADDR6_INIT(0ul, 0ul, 0ul, 0ul);
|
||||
|
||||
#ifndef isprint
|
||||
#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up)
|
||||
#define isprint(c) in_range(c, 0x20, 0x7f)
|
||||
#define isdigit(c) in_range(c, '0', '9')
|
||||
#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
|
||||
#define islower(c) in_range(c, 'a', 'z')
|
||||
#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
|
||||
#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Check whether "cp" is a valid ascii representation
|
||||
* of an IPv6 address and convert to a binary address.
|
||||
* Returns 1 if the address is valid, 0 if not.
|
||||
*
|
||||
* @param cp IPv6 address in ascii representation (e.g. "FF01::1")
|
||||
* @param addr pointer to which to save the ip address in network order
|
||||
* @return 1 if cp could be converted to addr, 0 on failure
|
||||
*/
|
||||
int
|
||||
ip_addr_maskcmp(struct ip_addr *addr1, struct ip_addr *addr2,
|
||||
struct ip_addr *mask)
|
||||
ip6addr_aton(const char *cp, ip6_addr_t *addr)
|
||||
{
|
||||
return((addr1->addr[0] & mask->addr[0]) == (addr2->addr[0] & mask->addr[0]) &&
|
||||
(addr1->addr[1] & mask->addr[1]) == (addr2->addr[1] & mask->addr[1]) &&
|
||||
(addr1->addr[2] & mask->addr[2]) == (addr2->addr[2] & mask->addr[2]) &&
|
||||
(addr1->addr[3] & mask->addr[3]) == (addr2->addr[3] & mask->addr[3]));
|
||||
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
int
|
||||
ip_addr_cmp(struct ip_addr *addr1, struct ip_addr *addr2)
|
||||
{
|
||||
return(addr1->addr[0] == addr2->addr[0] &&
|
||||
addr1->addr[1] == addr2->addr[1] &&
|
||||
addr1->addr[2] == addr2->addr[2] &&
|
||||
addr1->addr[3] == addr2->addr[3]);
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void
|
||||
ip_addr_set(struct ip_addr *dest, struct ip_addr *src)
|
||||
{
|
||||
memcpy(dest, src, sizeof(struct ip_addr));
|
||||
/* dest->addr[0] = src->addr[0];
|
||||
dest->addr[1] = src->addr[1];
|
||||
dest->addr[2] = src->addr[2];
|
||||
dest->addr[3] = src->addr[3];*/
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
int
|
||||
ip_addr_isany(struct ip_addr *addr)
|
||||
{
|
||||
if (addr == NULL) return 1;
|
||||
return((addr->addr[0] | addr->addr[1] | addr->addr[2] | addr->addr[3]) == 0);
|
||||
u32_t addr_index, zero_blocks, current_block_index, current_block_value;
|
||||
const char *s;
|
||||
|
||||
/* Count the number of colons, to count the number of blocks in a "::" sequence
|
||||
zero_blocks may be 1 even if there are no :: sequences */
|
||||
zero_blocks = 8;
|
||||
for (s = cp; *s != 0; s++) {
|
||||
if (*s == ':') {
|
||||
zero_blocks--;
|
||||
} else if (!isxdigit(*s)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* parse each block */
|
||||
addr_index = 0;
|
||||
current_block_index = 0;
|
||||
current_block_value = 0;
|
||||
for (s = cp; *s != 0; s++) {
|
||||
if (*s == ':') {
|
||||
if (addr) {
|
||||
if (current_block_index & 0x1) {
|
||||
addr->addr[addr_index++] |= current_block_value;
|
||||
}
|
||||
else {
|
||||
addr->addr[addr_index] = current_block_value << 16;
|
||||
}
|
||||
}
|
||||
current_block_index++;
|
||||
current_block_value = 0;
|
||||
if (current_block_index > 7) {
|
||||
/* address too long! */
|
||||
return 0;
|
||||
}
|
||||
if (s[1] == ':') {
|
||||
if (s[2] == ':') {
|
||||
/* invalid format: three successive colons */
|
||||
return 0;
|
||||
}
|
||||
s++;
|
||||
/* "::" found, set zeros */
|
||||
while (zero_blocks > 0) {
|
||||
zero_blocks--;
|
||||
if (current_block_index & 0x1) {
|
||||
addr_index++;
|
||||
} else {
|
||||
if (addr) {
|
||||
addr->addr[addr_index] = 0;
|
||||
}
|
||||
}
|
||||
current_block_index++;
|
||||
if (current_block_index > 7) {
|
||||
/* address too long! */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isxdigit(*s)) {
|
||||
/* add current digit */
|
||||
current_block_value = (current_block_value << 4) +
|
||||
(isdigit(*s) ? *s - '0' :
|
||||
10 + (islower(*s) ? *s - 'a' : *s - 'A'));
|
||||
} else {
|
||||
/* unexpected digit, space? CRLF? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr) {
|
||||
if (current_block_index & 0x1) {
|
||||
addr->addr[addr_index++] |= current_block_value;
|
||||
}
|
||||
else {
|
||||
addr->addr[addr_index] = current_block_value << 16;
|
||||
}
|
||||
}
|
||||
|
||||
/* convert to network byte order. */
|
||||
if (addr) {
|
||||
for (addr_index = 0; addr_index < 4; addr_index++) {
|
||||
addr->addr[addr_index] = lwip_htonl(addr->addr[addr_index]);
|
||||
}
|
||||
}
|
||||
|
||||
if (current_block_index != 7) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
/*#if IP_DEBUG*/
|
||||
void
|
||||
ip_addr_debug_print(struct ip_addr *addr)
|
||||
/**
|
||||
* Convert numeric IPv6 address into ASCII representation.
|
||||
* returns ptr to static buffer; not reentrant!
|
||||
*
|
||||
* @param addr ip6 address in network order to convert
|
||||
* @return pointer to a global static (!) buffer that holds the ASCII
|
||||
* representation of addr
|
||||
*/
|
||||
char *
|
||||
ip6addr_ntoa(const ip6_addr_t *addr)
|
||||
{
|
||||
printf("%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx",
|
||||
ntohl(addr->addr[0]) >> 16 & 0xffff,
|
||||
ntohl(addr->addr[0]) & 0xffff,
|
||||
ntohl(addr->addr[1]) >> 16 & 0xffff,
|
||||
ntohl(addr->addr[1]) & 0xffff,
|
||||
ntohl(addr->addr[2]) >> 16 & 0xffff,
|
||||
ntohl(addr->addr[2]) & 0xffff,
|
||||
ntohl(addr->addr[3]) >> 16 & 0xffff,
|
||||
ntohl(addr->addr[3]) & 0xffff);
|
||||
static char str[40];
|
||||
return ip6addr_ntoa_r(addr, str, 40);
|
||||
}
|
||||
/*#endif*/ /* IP_DEBUG */
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
|
||||
*
|
||||
* @param addr ip6 address in network order to convert
|
||||
* @param buf target buffer where the string is stored
|
||||
* @param buflen length of buf
|
||||
* @return either pointer to buf which now holds the ASCII
|
||||
* representation of addr or NULL if buf was too small
|
||||
*/
|
||||
char *
|
||||
ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)
|
||||
{
|
||||
u32_t current_block_index, current_block_value, next_block_value;
|
||||
s32_t i;
|
||||
u8_t zero_flag, empty_block_flag;
|
||||
|
||||
i = 0;
|
||||
empty_block_flag = 0; /* used to indicate a zero chain for "::' */
|
||||
|
||||
for (current_block_index = 0; current_block_index < 8; current_block_index++) {
|
||||
/* get the current 16-bit block */
|
||||
current_block_value = lwip_htonl(addr->addr[current_block_index >> 1]);
|
||||
if ((current_block_index & 0x1) == 0) {
|
||||
current_block_value = current_block_value >> 16;
|
||||
}
|
||||
current_block_value &= 0xffff;
|
||||
|
||||
/* Check for empty block. */
|
||||
if (current_block_value == 0) {
|
||||
if (current_block_index == 7 && empty_block_flag == 1) {
|
||||
/* special case, we must render a ':' for the last block. */
|
||||
buf[i++] = ':';
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (empty_block_flag == 0) {
|
||||
/* generate empty block "::", but only if more than one contiguous zero block,
|
||||
* according to current formatting suggestions RFC 5952. */
|
||||
next_block_value = lwip_htonl(addr->addr[(current_block_index + 1) >> 1]);
|
||||
if ((current_block_index & 0x1) == 0x01) {
|
||||
next_block_value = next_block_value >> 16;
|
||||
}
|
||||
next_block_value &= 0xffff;
|
||||
if (next_block_value == 0) {
|
||||
empty_block_flag = 1;
|
||||
buf[i++] = ':';
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
continue; /* move on to next block. */
|
||||
}
|
||||
} else if (empty_block_flag == 1) {
|
||||
/* move on to next block. */
|
||||
continue;
|
||||
}
|
||||
} else if (empty_block_flag == 1) {
|
||||
/* Set this flag value so we don't produce multiple empty blocks. */
|
||||
empty_block_flag = 2;
|
||||
}
|
||||
|
||||
if (current_block_index > 0) {
|
||||
buf[i++] = ':';
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((current_block_value & 0xf000) == 0) {
|
||||
zero_flag = 1;
|
||||
} else {
|
||||
buf[i++] = xchar(((current_block_value & 0xf000) >> 12));
|
||||
zero_flag = 0;
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (((current_block_value & 0xf00) == 0) && (zero_flag)) {
|
||||
/* do nothing */
|
||||
} else {
|
||||
buf[i++] = xchar(((current_block_value & 0xf00) >> 8));
|
||||
zero_flag = 0;
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (((current_block_value & 0xf0) == 0) && (zero_flag)) {
|
||||
/* do nothing */
|
||||
}
|
||||
else {
|
||||
buf[i++] = xchar(((current_block_value & 0xf0) >> 4));
|
||||
zero_flag = 0;
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
buf[i++] = xchar((current_block_value & 0xf));
|
||||
if (i >= buflen) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
buf[i] = 0;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV6 */
|
||||
|
||||
777
src/core/ipv6/ip6_frag.c
Normal file
777
src/core/ipv6/ip6_frag.c
Normal file
@@ -0,0 +1,777 @@
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* IPv6 fragmentation and reassembly.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/ip6_frag.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/icmp6.h"
|
||||
#include "lwip/nd6.h"
|
||||
#include "lwip/ip.h"
|
||||
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
|
||||
/** Setting this to 0, you can turn off checking the fragments for overlapping
|
||||
* regions. The code gets a little smaller. Only use this if you know that
|
||||
* overlapping won't occur on your network! */
|
||||
#ifndef IP_REASS_CHECK_OVERLAP
|
||||
#define IP_REASS_CHECK_OVERLAP 1
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
|
||||
/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
|
||||
* full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
|
||||
* Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
|
||||
* is set to 1, so one datagram can be reassembled at a time, only. */
|
||||
#ifndef IP_REASS_FREE_OLDEST
|
||||
#define IP_REASS_FREE_OLDEST 1
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
#define IPV6_FRAG_REQROOM ((s16_t)(sizeof(struct ip6_reass_helper) - IP6_FRAG_HLEN))
|
||||
#endif
|
||||
|
||||
#define IP_REASS_FLAG_LASTFRAG 0x01
|
||||
|
||||
/** This is a helper struct which holds the starting
|
||||
* offset and the ending offset of this fragment to
|
||||
* easily chain the fragments.
|
||||
* It has the same packing requirements as the IPv6 header, since it replaces
|
||||
* the Fragment Header in memory in incoming fragments to keep
|
||||
* track of the various fragments.
|
||||
*/
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/bpstruct.h"
|
||||
#endif
|
||||
PACK_STRUCT_BEGIN
|
||||
struct ip6_reass_helper {
|
||||
PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
|
||||
PACK_STRUCT_FIELD(u16_t start);
|
||||
PACK_STRUCT_FIELD(u16_t end);
|
||||
} PACK_STRUCT_STRUCT;
|
||||
PACK_STRUCT_END
|
||||
#ifdef PACK_STRUCT_USE_INCLUDES
|
||||
# include "arch/epstruct.h"
|
||||
#endif
|
||||
|
||||
/* static variables */
|
||||
static struct ip6_reassdata *reassdatagrams;
|
||||
static u16_t ip6_reass_pbufcount;
|
||||
|
||||
/* Forward declarations. */
|
||||
static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr);
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed);
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
|
||||
void
|
||||
ip6_reass_tmr(void)
|
||||
{
|
||||
struct ip6_reassdata *r, *tmp;
|
||||
|
||||
#if !IPV6_FRAG_COPYHEADER
|
||||
LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
|
||||
sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
|
||||
#endif /* !IPV6_FRAG_COPYHEADER */
|
||||
|
||||
r = reassdatagrams;
|
||||
while (r != NULL) {
|
||||
/* Decrement the timer. Once it reaches 0,
|
||||
* clean up the incomplete fragment assembly */
|
||||
if (r->timer > 0) {
|
||||
r->timer--;
|
||||
r = r->next;
|
||||
} else {
|
||||
/* reassembly timed out */
|
||||
tmp = r;
|
||||
/* get the next pointer before freeing */
|
||||
r = r->next;
|
||||
/* free the helper struct and all enqueued pbufs */
|
||||
ip6_reass_free_complete_datagram(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a datagram (struct ip6_reassdata) and all its pbufs.
|
||||
* Updates the total count of enqueued pbufs (ip6_reass_pbufcount),
|
||||
* sends an ICMP time exceeded packet.
|
||||
*
|
||||
* @param ipr datagram to free
|
||||
*/
|
||||
static void
|
||||
ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
|
||||
{
|
||||
struct ip6_reassdata *prev;
|
||||
u16_t pbufs_freed = 0;
|
||||
u16_t clen;
|
||||
struct pbuf *p;
|
||||
struct ip6_reass_helper *iprh;
|
||||
|
||||
#if LWIP_ICMP6
|
||||
iprh = (struct ip6_reass_helper *)ipr->p->payload;
|
||||
if (iprh->start == 0) {
|
||||
/* The first fragment was received, send ICMP time exceeded. */
|
||||
/* First, de-queue the first pbuf from r->p. */
|
||||
p = ipr->p;
|
||||
ipr->p = iprh->next_pbuf;
|
||||
/* Then, move back to the original ipv6 header (we are now pointing to Fragment header).
|
||||
This cannot fail since we already checked when receiving this fragment. */
|
||||
if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)IPV6_FRAG_HDRREF(ipr->iphdr)))) {
|
||||
LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0);
|
||||
}
|
||||
else {
|
||||
icmp6_time_exceeded(p, ICMP6_TE_FRAG);
|
||||
}
|
||||
clen = pbuf_clen(p);
|
||||
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
|
||||
pbufs_freed += clen;
|
||||
pbuf_free(p);
|
||||
}
|
||||
#endif /* LWIP_ICMP6 */
|
||||
|
||||
/* First, free all received pbufs. The individual pbufs need to be released
|
||||
separately as they have not yet been chained */
|
||||
p = ipr->p;
|
||||
while (p != NULL) {
|
||||
struct pbuf *pcur;
|
||||
iprh = (struct ip6_reass_helper *)p->payload;
|
||||
pcur = p;
|
||||
/* get the next pointer before freeing */
|
||||
p = iprh->next_pbuf;
|
||||
clen = pbuf_clen(pcur);
|
||||
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
|
||||
pbufs_freed += clen;
|
||||
pbuf_free(pcur);
|
||||
}
|
||||
|
||||
/* Then, unchain the struct ip6_reassdata from the list and free it. */
|
||||
if (ipr == reassdatagrams) {
|
||||
reassdatagrams = ipr->next;
|
||||
} else {
|
||||
prev = reassdatagrams;
|
||||
while (prev != NULL) {
|
||||
if (prev->next == ipr) {
|
||||
break;
|
||||
}
|
||||
prev = prev->next;
|
||||
}
|
||||
if (prev != NULL) {
|
||||
prev->next = ipr->next;
|
||||
}
|
||||
}
|
||||
memp_free(MEMP_IP6_REASSDATA, ipr);
|
||||
|
||||
/* Finally, update number of pbufs in reassembly queue */
|
||||
LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed);
|
||||
ip6_reass_pbufcount -= pbufs_freed;
|
||||
}
|
||||
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
/**
|
||||
* Free the oldest datagram to make room for enqueueing new fragments.
|
||||
* The datagram ipr is not freed!
|
||||
*
|
||||
* @param ipr ip6_reassdata for the current fragment
|
||||
* @param pbufs_needed number of pbufs needed to enqueue
|
||||
* (used for freeing other datagrams if not enough space)
|
||||
*/
|
||||
static void
|
||||
ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed)
|
||||
{
|
||||
struct ip6_reassdata *r, *oldest;
|
||||
|
||||
/* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
|
||||
* but don't free the current datagram! */
|
||||
do {
|
||||
r = oldest = reassdatagrams;
|
||||
while (r != NULL) {
|
||||
if (r != ipr) {
|
||||
if (r->timer <= oldest->timer) {
|
||||
/* older than the previous oldest */
|
||||
oldest = r;
|
||||
}
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
if (oldest == ipr) {
|
||||
/* nothing to free, ipr is the only element on the list */
|
||||
return;
|
||||
}
|
||||
if (oldest != NULL) {
|
||||
ip6_reass_free_complete_datagram(oldest);
|
||||
}
|
||||
} while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL));
|
||||
}
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
|
||||
/**
|
||||
* Reassembles incoming IPv6 fragments into an IPv6 datagram.
|
||||
*
|
||||
* @param p points to the IPv6 Fragment Header
|
||||
* @return NULL if reassembly is incomplete, pbuf pointing to
|
||||
* IPv6 Header if reassembly is complete
|
||||
*/
|
||||
struct pbuf *
|
||||
ip6_reass(struct pbuf *p)
|
||||
{
|
||||
struct ip6_reassdata *ipr, *ipr_prev;
|
||||
struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
|
||||
struct ip6_frag_hdr *frag_hdr;
|
||||
u16_t offset, len;
|
||||
u16_t clen;
|
||||
u8_t valid = 1;
|
||||
struct pbuf *q;
|
||||
|
||||
IP6_FRAG_STATS_INC(ip6_frag.recv);
|
||||
|
||||
if ((const void*)ip6_current_header() != ((u8_t*)p->payload) - IP6_HLEN) {
|
||||
/* ip6_frag_hdr must be in the first pbuf, not chained */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
}
|
||||
|
||||
frag_hdr = (struct ip6_frag_hdr *) p->payload;
|
||||
|
||||
clen = pbuf_clen(p);
|
||||
|
||||
offset = lwip_ntohs(frag_hdr->_fragment_offset);
|
||||
|
||||
/* Calculate fragment length from IPv6 payload length.
|
||||
* Adjust for headers before Fragment Header.
|
||||
* And finally adjust by Fragment Header length. */
|
||||
len = lwip_ntohs(ip6_current_header()->_plen);
|
||||
len -= (u16_t)(((u8_t*)p->payload - (const u8_t*)ip6_current_header()) - IP6_HLEN);
|
||||
len -= IP6_FRAG_HLEN;
|
||||
|
||||
/* Look for the datagram the fragment belongs to in the current datagram queue,
|
||||
* remembering the previous in the queue for later dequeueing. */
|
||||
for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) {
|
||||
/* Check if the incoming fragment matches the one currently present
|
||||
in the reassembly buffer. If so, we proceed with copying the
|
||||
fragment into the buffer. */
|
||||
if ((frag_hdr->_identification == ipr->identification) &&
|
||||
ip6_addr_cmp(ip6_current_src_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->src)) &&
|
||||
ip6_addr_cmp(ip6_current_dest_addr(), &(IPV6_FRAG_HDRREF(ipr->iphdr)->dest))) {
|
||||
IP6_FRAG_STATS_INC(ip6_frag.cachehit);
|
||||
break;
|
||||
}
|
||||
ipr_prev = ipr;
|
||||
}
|
||||
|
||||
if (ipr == NULL) {
|
||||
/* Enqueue a new datagram into the datagram queue */
|
||||
ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
|
||||
if (ipr == NULL) {
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
/* Make room and try again. */
|
||||
ip6_reass_remove_oldest_datagram(ipr, clen);
|
||||
ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
|
||||
if (ipr != NULL) {
|
||||
/* re-search ipr_prev since it might have been removed */
|
||||
for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
|
||||
if (ipr_prev->next == ipr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
{
|
||||
IP6_FRAG_STATS_INC(ip6_frag.memerr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
}
|
||||
}
|
||||
|
||||
memset(ipr, 0, sizeof(struct ip6_reassdata));
|
||||
ipr->timer = IP_REASS_MAXAGE;
|
||||
|
||||
/* enqueue the new structure to the front of the list */
|
||||
ipr->next = reassdatagrams;
|
||||
reassdatagrams = ipr;
|
||||
|
||||
/* Use the current IPv6 header for src/dest address reference.
|
||||
* Eventually, we will replace it when we get the first fragment
|
||||
* (it might be this one, in any case, it is done later). */
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
|
||||
#else /* IPV6_FRAG_COPYHEADER */
|
||||
/* need to use the none-const pointer here: */
|
||||
ipr->iphdr = ip_data.current_ip6_header;
|
||||
#endif /* IPV6_FRAG_COPYHEADER */
|
||||
|
||||
/* copy the fragmented packet id. */
|
||||
ipr->identification = frag_hdr->_identification;
|
||||
|
||||
/* copy the nexth field */
|
||||
ipr->nexth = frag_hdr->_nexth;
|
||||
}
|
||||
|
||||
/* Check if we are allowed to enqueue more datagrams. */
|
||||
if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
|
||||
#if IP_REASS_FREE_OLDEST
|
||||
ip6_reass_remove_oldest_datagram(ipr, clen);
|
||||
if ((ip6_reass_pbufcount + clen) <= IP_REASS_MAX_PBUFS) {
|
||||
/* re-search ipr_prev since it might have been removed */
|
||||
for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
|
||||
if (ipr_prev->next == ipr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else
|
||||
#endif /* IP_REASS_FREE_OLDEST */
|
||||
{
|
||||
/* @todo: send ICMPv6 time exceeded here? */
|
||||
/* drop this pbuf */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.memerr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overwrite Fragment Header with our own helper struct. */
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
if (IPV6_FRAG_REQROOM > 0) {
|
||||
/* Make room for struct ip6_reass_helper (only required if sizeof(void*) > 4).
|
||||
This cannot fail since we already checked when receiving this fragment. */
|
||||
u8_t hdrerr = pbuf_header_force(p, IPV6_FRAG_REQROOM);
|
||||
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
|
||||
}
|
||||
#else /* IPV6_FRAG_COPYHEADER */
|
||||
LWIP_ASSERT("sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN, set IPV6_FRAG_COPYHEADER to 1",
|
||||
sizeof(struct ip6_reass_helper) <= IP6_FRAG_HLEN);
|
||||
#endif /* IPV6_FRAG_COPYHEADER */
|
||||
iprh = (struct ip6_reass_helper *)p->payload;
|
||||
iprh->next_pbuf = NULL;
|
||||
iprh->start = (offset & IP6_FRAG_OFFSET_MASK);
|
||||
iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len;
|
||||
|
||||
/* find the right place to insert this pbuf */
|
||||
/* Iterate through until we either get to the end of the list (append),
|
||||
* or we find on with a larger offset (insert). */
|
||||
for (q = ipr->p; q != NULL;) {
|
||||
iprh_tmp = (struct ip6_reass_helper*)q->payload;
|
||||
if (iprh->start < iprh_tmp->start) {
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
if (iprh->end > iprh_tmp->start) {
|
||||
/* fragment overlaps with following, throw away */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
}
|
||||
if (iprh_prev != NULL) {
|
||||
if (iprh->start < iprh_prev->end) {
|
||||
/* fragment overlaps with previous, throw away */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
}
|
||||
}
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
/* the new pbuf should be inserted before this */
|
||||
iprh->next_pbuf = q;
|
||||
if (iprh_prev != NULL) {
|
||||
/* not the fragment with the lowest offset */
|
||||
iprh_prev->next_pbuf = p;
|
||||
} else {
|
||||
/* fragment with the lowest offset */
|
||||
ipr->p = p;
|
||||
}
|
||||
break;
|
||||
} else if (iprh->start == iprh_tmp->start) {
|
||||
/* received the same datagram twice: no need to keep the datagram */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
} else if (iprh->start < iprh_tmp->end) {
|
||||
/* overlap: no need to keep the new datagram */
|
||||
IP6_FRAG_STATS_INC(ip6_frag.proterr);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.drop);
|
||||
goto nullreturn;
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
} else {
|
||||
/* Check if the fragments received so far have no gaps. */
|
||||
if (iprh_prev != NULL) {
|
||||
if (iprh_prev->end != iprh_tmp->start) {
|
||||
/* There is a fragment missing between the current
|
||||
* and the previous fragment */
|
||||
valid = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
q = iprh_tmp->next_pbuf;
|
||||
iprh_prev = iprh_tmp;
|
||||
}
|
||||
|
||||
/* If q is NULL, then we made it to the end of the list. Determine what to do now */
|
||||
if (q == NULL) {
|
||||
if (iprh_prev != NULL) {
|
||||
/* this is (for now), the fragment with the highest offset:
|
||||
* chain it to the last fragment */
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
iprh_prev->next_pbuf = p;
|
||||
if (iprh_prev->end != iprh->start) {
|
||||
valid = 0;
|
||||
}
|
||||
} else {
|
||||
#if IP_REASS_CHECK_OVERLAP
|
||||
LWIP_ASSERT("no previous fragment, this must be the first fragment!",
|
||||
ipr->p == NULL);
|
||||
#endif /* IP_REASS_CHECK_OVERLAP */
|
||||
/* this is the first fragment we ever received for this ip datagram */
|
||||
ipr->p = p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Track the current number of pbufs current 'in-flight', in order to limit
|
||||
the number of fragments that may be enqueued at any one time */
|
||||
ip6_reass_pbufcount += clen;
|
||||
|
||||
/* Remember IPv6 header if this is the first fragment. */
|
||||
if (iprh->start == 0) {
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
if (iprh->next_pbuf != NULL) {
|
||||
MEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
|
||||
}
|
||||
#else /* IPV6_FRAG_COPYHEADER */
|
||||
/* need to use the none-const pointer here: */
|
||||
ipr->iphdr = ip_data.current_ip6_header;
|
||||
#endif /* IPV6_FRAG_COPYHEADER */
|
||||
}
|
||||
|
||||
/* If this is the last fragment, calculate total packet length. */
|
||||
if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
|
||||
ipr->datagram_len = iprh->end;
|
||||
}
|
||||
|
||||
/* Additional validity tests: we have received first and last fragment. */
|
||||
iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload;
|
||||
if (iprh_tmp->start != 0) {
|
||||
valid = 0;
|
||||
}
|
||||
if (ipr->datagram_len == 0) {
|
||||
valid = 0;
|
||||
}
|
||||
|
||||
/* Final validity test: no gaps between current and last fragment. */
|
||||
iprh_prev = iprh;
|
||||
q = iprh->next_pbuf;
|
||||
while ((q != NULL) && valid) {
|
||||
iprh = (struct ip6_reass_helper*)q->payload;
|
||||
if (iprh_prev->end != iprh->start) {
|
||||
valid = 0;
|
||||
break;
|
||||
}
|
||||
iprh_prev = iprh;
|
||||
q = iprh->next_pbuf;
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
/* All fragments have been received */
|
||||
struct ip6_hdr* iphdr_ptr;
|
||||
|
||||
/* chain together the pbufs contained within the ip6_reassdata list. */
|
||||
iprh = (struct ip6_reass_helper*) ipr->p->payload;
|
||||
while (iprh != NULL) {
|
||||
struct pbuf* next_pbuf = iprh->next_pbuf;
|
||||
if (next_pbuf != NULL) {
|
||||
/* Save next helper struct (will be hidden in next step). */
|
||||
iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload;
|
||||
|
||||
/* hide the fragment header for every succeeding fragment */
|
||||
pbuf_header(next_pbuf, -IP6_FRAG_HLEN);
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
if (IPV6_FRAG_REQROOM > 0) {
|
||||
/* hide the extra bytes borrowed from ip6_hdr for struct ip6_reass_helper */
|
||||
u8_t hdrerr = pbuf_header(next_pbuf, -(s16_t)(IPV6_FRAG_REQROOM));
|
||||
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
|
||||
}
|
||||
#endif
|
||||
pbuf_cat(ipr->p, next_pbuf);
|
||||
}
|
||||
else {
|
||||
iprh_tmp = NULL;
|
||||
}
|
||||
|
||||
iprh = iprh_tmp;
|
||||
}
|
||||
|
||||
#if IPV6_FRAG_COPYHEADER
|
||||
if (IPV6_FRAG_REQROOM > 0) {
|
||||
/* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */
|
||||
u8_t hdrerr = pbuf_header(ipr->p, -(s16_t)(IPV6_FRAG_REQROOM));
|
||||
LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
|
||||
}
|
||||
iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->p->payload - IP6_HLEN);
|
||||
MEMCPY(iphdr_ptr, &ipr->iphdr, IP6_HLEN);
|
||||
#else
|
||||
iphdr_ptr = ipr->iphdr;
|
||||
#endif
|
||||
|
||||
/* Adjust datagram length by adding header lengths. */
|
||||
ipr->datagram_len += (u16_t)(((u8_t*)ipr->p->payload - (u8_t*)iphdr_ptr)
|
||||
+ IP6_FRAG_HLEN
|
||||
- IP6_HLEN);
|
||||
|
||||
/* Set payload length in ip header. */
|
||||
iphdr_ptr->_plen = lwip_htons(ipr->datagram_len);
|
||||
|
||||
/* Get the first pbuf. */
|
||||
p = ipr->p;
|
||||
|
||||
/* Restore Fragment Header in first pbuf. Mark as "single fragment"
|
||||
* packet. Restore nexth. */
|
||||
frag_hdr = (struct ip6_frag_hdr *) p->payload;
|
||||
frag_hdr->_nexth = ipr->nexth;
|
||||
frag_hdr->reserved = 0;
|
||||
frag_hdr->_fragment_offset = 0;
|
||||
frag_hdr->_identification = 0;
|
||||
|
||||
/* release the sources allocate for the fragment queue entry */
|
||||
if (reassdatagrams == ipr) {
|
||||
/* it was the first in the list */
|
||||
reassdatagrams = ipr->next;
|
||||
} else {
|
||||
/* it wasn't the first, so it must have a valid 'prev' */
|
||||
LWIP_ASSERT("sanity check linked list", ipr_prev != NULL);
|
||||
ipr_prev->next = ipr->next;
|
||||
}
|
||||
memp_free(MEMP_IP6_REASSDATA, ipr);
|
||||
|
||||
/* adjust the number of pbufs currently queued for reassembly. */
|
||||
ip6_reass_pbufcount -= pbuf_clen(p);
|
||||
|
||||
/* Move pbuf back to IPv6 header.
|
||||
This cannot fail since we already checked when receiving this fragment. */
|
||||
if (pbuf_header_force(p, (s16_t)((u8_t*)p->payload - (u8_t*)iphdr_ptr))) {
|
||||
LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0);
|
||||
pbuf_free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return the pbuf chain */
|
||||
return p;
|
||||
}
|
||||
/* the datagram is not (yet?) reassembled completely */
|
||||
return NULL;
|
||||
|
||||
nullreturn:
|
||||
pbuf_free(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
|
||||
|
||||
#if LWIP_IPV6 && LWIP_IPV6_FRAG
|
||||
|
||||
/** Allocate a new struct pbuf_custom_ref */
|
||||
static struct pbuf_custom_ref*
|
||||
ip6_frag_alloc_pbuf_custom_ref(void)
|
||||
{
|
||||
return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
|
||||
}
|
||||
|
||||
/** Free a struct pbuf_custom_ref */
|
||||
static void
|
||||
ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
|
||||
{
|
||||
LWIP_ASSERT("p != NULL", p != NULL);
|
||||
memp_free(MEMP_FRAG_PBUF, p);
|
||||
}
|
||||
|
||||
/** Free-callback function to free a 'struct pbuf_custom_ref', called by
|
||||
* pbuf_free. */
|
||||
static void
|
||||
ip6_frag_free_pbuf_custom(struct pbuf *p)
|
||||
{
|
||||
struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
|
||||
LWIP_ASSERT("pcr != NULL", pcr != NULL);
|
||||
LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
|
||||
if (pcr->original != NULL) {
|
||||
pbuf_free(pcr->original);
|
||||
}
|
||||
ip6_frag_free_pbuf_custom_ref(pcr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragment an IPv6 datagram if too large for the netif or path MTU.
|
||||
*
|
||||
* Chop the datagram in MTU sized chunks and send them in order
|
||||
* by pointing PBUF_REFs into p
|
||||
*
|
||||
* @param p ipv6 packet to send
|
||||
* @param netif the netif on which to send
|
||||
* @param dest destination ipv6 address to which to send
|
||||
*
|
||||
* @return ERR_OK if sent successfully, err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
ip6_frag(struct pbuf *p, struct netif *netif, const ip6_addr_t *dest)
|
||||
{
|
||||
struct ip6_hdr *original_ip6hdr;
|
||||
struct ip6_hdr *ip6hdr;
|
||||
struct ip6_frag_hdr *frag_hdr;
|
||||
struct pbuf *rambuf;
|
||||
struct pbuf *newpbuf;
|
||||
static u32_t identification;
|
||||
u16_t nfb;
|
||||
u16_t left, cop;
|
||||
u16_t mtu;
|
||||
u16_t fragment_offset = 0;
|
||||
u16_t last;
|
||||
u16_t poff = IP6_HLEN;
|
||||
u16_t newpbuflen = 0;
|
||||
u16_t left_to_copy;
|
||||
|
||||
identification++;
|
||||
|
||||
original_ip6hdr = (struct ip6_hdr *)p->payload;
|
||||
|
||||
mtu = nd6_get_destination_mtu(dest, netif);
|
||||
|
||||
/* @todo we assume there are no options in the unfragmentable part (IPv6 header). */
|
||||
left = p->tot_len - IP6_HLEN;
|
||||
|
||||
nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK;
|
||||
|
||||
while (left) {
|
||||
last = (left <= nfb);
|
||||
|
||||
/* Fill this fragment */
|
||||
cop = last ? left : nfb;
|
||||
|
||||
/* When not using a static buffer, create a chain of pbufs.
|
||||
* The first will be a PBUF_RAM holding the link, IPv6, and Fragment header.
|
||||
* The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
|
||||
* but limited to the size of an mtu.
|
||||
*/
|
||||
rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM);
|
||||
if (rambuf == NULL) {
|
||||
IP6_FRAG_STATS_INC(ip6_frag.memerr);
|
||||
return ERR_MEM;
|
||||
}
|
||||
LWIP_ASSERT("this needs a pbuf in one piece!",
|
||||
(p->len >= (IP6_HLEN)));
|
||||
SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
|
||||
ip6hdr = (struct ip6_hdr *)rambuf->payload;
|
||||
frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
|
||||
|
||||
/* Can just adjust p directly for needed offset. */
|
||||
p->payload = (u8_t *)p->payload + poff;
|
||||
p->len -= poff;
|
||||
p->tot_len -= poff;
|
||||
|
||||
left_to_copy = cop;
|
||||
while (left_to_copy) {
|
||||
struct pbuf_custom_ref *pcr;
|
||||
newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
|
||||
/* Is this pbuf already empty? */
|
||||
if (!newpbuflen) {
|
||||
p = p->next;
|
||||
continue;
|
||||
}
|
||||
pcr = ip6_frag_alloc_pbuf_custom_ref();
|
||||
if (pcr == NULL) {
|
||||
pbuf_free(rambuf);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.memerr);
|
||||
return ERR_MEM;
|
||||
}
|
||||
/* Mirror this pbuf, although we might not need all of it. */
|
||||
newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
|
||||
if (newpbuf == NULL) {
|
||||
ip6_frag_free_pbuf_custom_ref(pcr);
|
||||
pbuf_free(rambuf);
|
||||
IP6_FRAG_STATS_INC(ip6_frag.memerr);
|
||||
return ERR_MEM;
|
||||
}
|
||||
pbuf_ref(p);
|
||||
pcr->original = p;
|
||||
pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom;
|
||||
|
||||
/* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
|
||||
* so that it is removed when pbuf_dechain is later called on rambuf.
|
||||
*/
|
||||
pbuf_cat(rambuf, newpbuf);
|
||||
left_to_copy -= newpbuflen;
|
||||
if (left_to_copy) {
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
poff = newpbuflen;
|
||||
|
||||
/* Set headers */
|
||||
frag_hdr->_nexth = original_ip6hdr->_nexth;
|
||||
frag_hdr->reserved = 0;
|
||||
frag_hdr->_fragment_offset = lwip_htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG));
|
||||
frag_hdr->_identification = lwip_htonl(identification);
|
||||
|
||||
IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
|
||||
IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN);
|
||||
|
||||
/* No need for separate header pbuf - we allowed room for it in rambuf
|
||||
* when allocated.
|
||||
*/
|
||||
IP6_FRAG_STATS_INC(ip6_frag.xmit);
|
||||
netif->output_ip6(netif, rambuf, dest);
|
||||
|
||||
/* Unfortunately we can't reuse rambuf - the hardware may still be
|
||||
* using the buffer. Instead we free it (and the ensuing chain) and
|
||||
* recreate it next time round the loop. If we're lucky the hardware
|
||||
* will have already sent the packet, the free will really free, and
|
||||
* there will be zero memory penalty.
|
||||
*/
|
||||
|
||||
pbuf_free(rambuf);
|
||||
left -= cop;
|
||||
fragment_offset += cop;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */
|
||||
583
src/core/ipv6/mld6.c
Normal file
583
src/core/ipv6/mld6.c
Normal file
@@ -0,0 +1,583 @@
|
||||
/**
|
||||
* @file
|
||||
* Multicast listener discovery
|
||||
*
|
||||
* @defgroup mld6 MLD6
|
||||
* @ingroup ip6
|
||||
* Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710.
|
||||
* No support for MLDv2.\n
|
||||
* To be called from TCPIP thread
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010 Inico Technologies Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
* Author: Ivan Delamer <delamer@inicotech.com>
|
||||
*
|
||||
*
|
||||
* Please coordinate changes and requests with Ivan Delamer
|
||||
* <delamer@inicotech.com>
|
||||
*/
|
||||
|
||||
/* Based on igmp.c implementation of igmp v2 protocol */
|
||||
|
||||
#include "lwip/opt.h"
|
||||
|
||||
#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */
|
||||
|
||||
#include "lwip/mld6.h"
|
||||
#include "lwip/prot/mld6.h"
|
||||
#include "lwip/icmp6.h"
|
||||
#include "lwip/ip6.h"
|
||||
#include "lwip/ip6_addr.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/inet_chksum.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/memp.h"
|
||||
#include "lwip/stats.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/*
|
||||
* MLD constants
|
||||
*/
|
||||
#define MLD6_HL 1
|
||||
#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500)
|
||||
|
||||
#define MLD6_GROUP_NON_MEMBER 0
|
||||
#define MLD6_GROUP_DELAYING_MEMBER 1
|
||||
#define MLD6_GROUP_IDLE_MEMBER 2
|
||||
|
||||
/* Forward declarations. */
|
||||
static struct mld_group *mld6_new_group(struct netif *ifp, const ip6_addr_t *addr);
|
||||
static err_t mld6_remove_group(struct netif *netif, struct mld_group *group);
|
||||
static void mld6_delayed_report(struct mld_group *group, u16_t maxresp);
|
||||
static void mld6_send(struct netif *netif, struct mld_group *group, u8_t type);
|
||||
|
||||
|
||||
/**
|
||||
* Stop MLD processing on interface
|
||||
*
|
||||
* @param netif network interface on which stop MLD processing
|
||||
*/
|
||||
err_t
|
||||
mld6_stop(struct netif *netif)
|
||||
{
|
||||
struct mld_group *group = netif_mld6_data(netif);
|
||||
|
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, NULL);
|
||||
|
||||
while (group != NULL) {
|
||||
struct mld_group *next = group->next; /* avoid use-after-free below */
|
||||
|
||||
/* disable the group at the MAC level */
|
||||
if (netif->mld_mac_filter != NULL) {
|
||||
netif->mld_mac_filter(netif, &(group->group_address), NETIF_DEL_MAC_FILTER);
|
||||
}
|
||||
|
||||
/* free group */
|
||||
memp_free(MEMP_MLD6_GROUP, group);
|
||||
|
||||
/* move to "next" */
|
||||
group = next;
|
||||
}
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report MLD memberships for this interface
|
||||
*
|
||||
* @param netif network interface on which report MLD memberships
|
||||
*/
|
||||
void
|
||||
mld6_report_groups(struct netif *netif)
|
||||
{
|
||||
struct mld_group *group = netif_mld6_data(netif);
|
||||
|
||||
while (group != NULL) {
|
||||
mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
|
||||
group = group->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a group that is joined on a netif
|
||||
*
|
||||
* @param ifp the network interface for which to look
|
||||
* @param addr the group ipv6 address to search for
|
||||
* @return a struct mld_group* if the group has been found,
|
||||
* NULL if the group wasn't found.
|
||||
*/
|
||||
struct mld_group *
|
||||
mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr)
|
||||
{
|
||||
struct mld_group *group = netif_mld6_data(ifp);
|
||||
|
||||
while (group != NULL) {
|
||||
if (ip6_addr_cmp(&(group->group_address), addr)) {
|
||||
return group;
|
||||
}
|
||||
group = group->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create a new group
|
||||
*
|
||||
* @param ifp the network interface for which to create
|
||||
* @param addr the new group ipv6
|
||||
* @return a struct mld_group*,
|
||||
* NULL on memory error.
|
||||
*/
|
||||
static struct mld_group *
|
||||
mld6_new_group(struct netif *ifp, const ip6_addr_t *addr)
|
||||
{
|
||||
struct mld_group *group;
|
||||
|
||||
group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP);
|
||||
if (group != NULL) {
|
||||
ip6_addr_set(&(group->group_address), addr);
|
||||
group->timer = 0; /* Not running */
|
||||
group->group_state = MLD6_GROUP_IDLE_MEMBER;
|
||||
group->last_reporter_flag = 0;
|
||||
group->use = 0;
|
||||
group->next = netif_mld6_data(ifp);
|
||||
|
||||
netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group);
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a group from the mld_group_list, but do not free it yet
|
||||
*
|
||||
* @param group the group to remove
|
||||
* @return ERR_OK if group was removed from the list, an err_t otherwise
|
||||
*/
|
||||
static err_t
|
||||
mld6_remove_group(struct netif *netif, struct mld_group *group)
|
||||
{
|
||||
err_t err = ERR_OK;
|
||||
|
||||
/* Is it the first group? */
|
||||
if (netif_mld6_data(netif) == group) {
|
||||
netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group->next);
|
||||
} else {
|
||||
/* look for group further down the list */
|
||||
struct mld_group *tmpGroup;
|
||||
for (tmpGroup = netif_mld6_data(netif); tmpGroup != NULL; tmpGroup = tmpGroup->next) {
|
||||
if (tmpGroup->next == group) {
|
||||
tmpGroup->next = group->next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Group not find group */
|
||||
if (tmpGroup == NULL) {
|
||||
err = ERR_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process an input MLD message. Called by icmp6_input.
|
||||
*
|
||||
* @param p the mld packet, p->payload pointing to the icmpv6 header
|
||||
* @param inp the netif on which this packet was received
|
||||
*/
|
||||
void
|
||||
mld6_input(struct pbuf *p, struct netif *inp)
|
||||
{
|
||||
struct mld_header *mld_hdr;
|
||||
struct mld_group *group;
|
||||
|
||||
MLD6_STATS_INC(mld6.recv);
|
||||
|
||||
/* Check that mld header fits in packet. */
|
||||
if (p->len < sizeof(struct mld_header)) {
|
||||
/* @todo debug message */
|
||||
pbuf_free(p);
|
||||
MLD6_STATS_INC(mld6.lenerr);
|
||||
MLD6_STATS_INC(mld6.drop);
|
||||
return;
|
||||
}
|
||||
|
||||
mld_hdr = (struct mld_header *)p->payload;
|
||||
|
||||
switch (mld_hdr->type) {
|
||||
case ICMP6_TYPE_MLQ: /* Multicast listener query. */
|
||||
/* Is it a general query? */
|
||||
if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
|
||||
ip6_addr_isany(&(mld_hdr->multicast_address))) {
|
||||
MLD6_STATS_INC(mld6.rx_general);
|
||||
/* Report all groups, except all nodes group, and if-local groups. */
|
||||
group = netif_mld6_data(inp);
|
||||
while (group != NULL) {
|
||||
if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
|
||||
(!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) {
|
||||
mld6_delayed_report(group, mld_hdr->max_resp_delay);
|
||||
}
|
||||
group = group->next;
|
||||
}
|
||||
} else {
|
||||
/* Have we joined this group?
|
||||
* We use IP6 destination address to have a memory aligned copy.
|
||||
* mld_hdr->multicast_address should be the same. */
|
||||
MLD6_STATS_INC(mld6.rx_group);
|
||||
group = mld6_lookfor_group(inp, ip6_current_dest_addr());
|
||||
if (group != NULL) {
|
||||
/* Schedule a report. */
|
||||
mld6_delayed_report(group, mld_hdr->max_resp_delay);
|
||||
}
|
||||
}
|
||||
break; /* ICMP6_TYPE_MLQ */
|
||||
case ICMP6_TYPE_MLR: /* Multicast listener report. */
|
||||
/* Have we joined this group?
|
||||
* We use IP6 destination address to have a memory aligned copy.
|
||||
* mld_hdr->multicast_address should be the same. */
|
||||
MLD6_STATS_INC(mld6.rx_report);
|
||||
group = mld6_lookfor_group(inp, ip6_current_dest_addr());
|
||||
if (group != NULL) {
|
||||
/* If we are waiting to report, cancel it. */
|
||||
if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
|
||||
group->timer = 0; /* stopped */
|
||||
group->group_state = MLD6_GROUP_IDLE_MEMBER;
|
||||
group->last_reporter_flag = 0;
|
||||
}
|
||||
}
|
||||
break; /* ICMP6_TYPE_MLR */
|
||||
case ICMP6_TYPE_MLD: /* Multicast listener done. */
|
||||
/* Do nothing, router will query us. */
|
||||
break; /* ICMP6_TYPE_MLD */
|
||||
default:
|
||||
MLD6_STATS_INC(mld6.proterr);
|
||||
MLD6_STATS_INC(mld6.drop);
|
||||
break;
|
||||
}
|
||||
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup mld6
|
||||
* Join a group on a network interface.
|
||||
*
|
||||
* @param srcaddr ipv6 address of the network interface which should
|
||||
* join a new group. If IP6_ADDR_ANY, join on all netifs
|
||||
* @param groupaddr the ipv6 address of the group to join
|
||||
* @return ERR_OK if group was joined on the netif(s), an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
|
||||
{
|
||||
err_t err = ERR_VAL; /* no matching interface */
|
||||
struct netif *netif;
|
||||
|
||||
/* loop through netif's */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
/* Should we join this interface ? */
|
||||
if (ip6_addr_isany(srcaddr) ||
|
||||
netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
|
||||
err = mld6_joingroup_netif(netif, groupaddr);
|
||||
if (err != ERR_OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* proceed to next network interface */
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup mld6
|
||||
* Join a group on a network interface.
|
||||
*
|
||||
* @param netif the network interface which should join a new group.
|
||||
* @param groupaddr the ipv6 address of the group to join
|
||||
* @return ERR_OK if group was joined on the netif, an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
|
||||
{
|
||||
struct mld_group *group;
|
||||
|
||||
/* find group or create a new one if not found */
|
||||
group = mld6_lookfor_group(netif, groupaddr);
|
||||
|
||||
if (group == NULL) {
|
||||
/* Joining a new group. Create a new group entry. */
|
||||
group = mld6_new_group(netif, groupaddr);
|
||||
if (group == NULL) {
|
||||
return ERR_MEM;
|
||||
}
|
||||
|
||||
/* Activate this address on the MAC layer. */
|
||||
if (netif->mld_mac_filter != NULL) {
|
||||
netif->mld_mac_filter(netif, groupaddr, NETIF_ADD_MAC_FILTER);
|
||||
}
|
||||
|
||||
/* Report our membership. */
|
||||
MLD6_STATS_INC(mld6.tx_report);
|
||||
mld6_send(netif, group, ICMP6_TYPE_MLR);
|
||||
mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
|
||||
}
|
||||
|
||||
/* Increment group use */
|
||||
group->use++;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup mld6
|
||||
* Leave a group on a network interface.
|
||||
*
|
||||
* @param srcaddr ipv6 address of the network interface which should
|
||||
* leave the group. If IP6_ISANY, leave on all netifs
|
||||
* @param groupaddr the ipv6 address of the group to leave
|
||||
* @return ERR_OK if group was left on the netif(s), an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
|
||||
{
|
||||
err_t err = ERR_VAL; /* no matching interface */
|
||||
struct netif *netif;
|
||||
|
||||
/* loop through netif's */
|
||||
netif = netif_list;
|
||||
while (netif != NULL) {
|
||||
/* Should we leave this interface ? */
|
||||
if (ip6_addr_isany(srcaddr) ||
|
||||
netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
|
||||
err_t res = mld6_leavegroup_netif(netif, groupaddr);
|
||||
if (err != ERR_OK) {
|
||||
/* Store this result if we have not yet gotten a success */
|
||||
err = res;
|
||||
}
|
||||
}
|
||||
/* proceed to next network interface */
|
||||
netif = netif->next;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup mld6
|
||||
* Leave a group on a network interface.
|
||||
*
|
||||
* @param netif the network interface which should leave the group.
|
||||
* @param groupaddr the ipv6 address of the group to leave
|
||||
* @return ERR_OK if group was left on the netif, an err_t otherwise
|
||||
*/
|
||||
err_t
|
||||
mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr)
|
||||
{
|
||||
struct mld_group *group;
|
||||
|
||||
/* find group */
|
||||
group = mld6_lookfor_group(netif, groupaddr);
|
||||
|
||||
if (group != NULL) {
|
||||
/* Leave if there is no other use of the group */
|
||||
if (group->use <= 1) {
|
||||
/* Remove the group from the list */
|
||||
mld6_remove_group(netif, group);
|
||||
|
||||
/* If we are the last reporter for this group */
|
||||
if (group->last_reporter_flag) {
|
||||
MLD6_STATS_INC(mld6.tx_leave);
|
||||
mld6_send(netif, group, ICMP6_TYPE_MLD);
|
||||
}
|
||||
|
||||
/* Disable the group at the MAC level */
|
||||
if (netif->mld_mac_filter != NULL) {
|
||||
netif->mld_mac_filter(netif, groupaddr, NETIF_DEL_MAC_FILTER);
|
||||
}
|
||||
|
||||
/* free group struct */
|
||||
memp_free(MEMP_MLD6_GROUP, group);
|
||||
} else {
|
||||
/* Decrement group use */
|
||||
group->use--;
|
||||
}
|
||||
|
||||
/* Left group */
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
/* Group not found */
|
||||
return ERR_VAL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Periodic timer for mld processing. Must be called every
|
||||
* MLD6_TMR_INTERVAL milliseconds (100).
|
||||
*
|
||||
* When a delaying member expires, a membership report is sent.
|
||||
*/
|
||||
void
|
||||
mld6_tmr(void)
|
||||
{
|
||||
struct netif *netif = netif_list;
|
||||
|
||||
while (netif != NULL) {
|
||||
struct mld_group *group = netif_mld6_data(netif);
|
||||
|
||||
while (group != NULL) {
|
||||
if (group->timer > 0) {
|
||||
group->timer--;
|
||||
if (group->timer == 0) {
|
||||
/* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
|
||||
if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
|
||||
MLD6_STATS_INC(mld6.tx_report);
|
||||
mld6_send(netif, group, ICMP6_TYPE_MLR);
|
||||
group->group_state = MLD6_GROUP_IDLE_MEMBER;
|
||||
}
|
||||
}
|
||||
}
|
||||
group = group->next;
|
||||
}
|
||||
netif = netif->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a delayed membership report for a group
|
||||
*
|
||||
* @param group the mld_group for which "delaying" membership report
|
||||
* should be sent
|
||||
* @param maxresp the max resp delay provided in the query
|
||||
*/
|
||||
static void
|
||||
mld6_delayed_report(struct mld_group *group, u16_t maxresp)
|
||||
{
|
||||
/* Convert maxresp from milliseconds to tmr ticks */
|
||||
maxresp = maxresp / MLD6_TMR_INTERVAL;
|
||||
if (maxresp == 0) {
|
||||
maxresp = 1;
|
||||
}
|
||||
|
||||
#ifdef LWIP_RAND
|
||||
/* Randomize maxresp. (if LWIP_RAND is supported) */
|
||||
maxresp = LWIP_RAND() % maxresp;
|
||||
if (maxresp == 0) {
|
||||
maxresp = 1;
|
||||
}
|
||||
#endif /* LWIP_RAND */
|
||||
|
||||
/* Apply timer value if no report has been scheduled already. */
|
||||
if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) ||
|
||||
((group->group_state == MLD6_GROUP_DELAYING_MEMBER) &&
|
||||
((group->timer == 0) || (maxresp < group->timer)))) {
|
||||
group->timer = maxresp;
|
||||
group->group_state = MLD6_GROUP_DELAYING_MEMBER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a MLD message (report or done).
|
||||
*
|
||||
* An IPv6 hop-by-hop options header with a router alert option
|
||||
* is prepended.
|
||||
*
|
||||
* @param group the group to report or quit
|
||||
* @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done)
|
||||
*/
|
||||
static void
|
||||
mld6_send(struct netif *netif, struct mld_group *group, u8_t type)
|
||||
{
|
||||
struct mld_header *mld_hdr;
|
||||
struct pbuf *p;
|
||||
const ip6_addr_t *src_addr;
|
||||
|
||||
/* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
|
||||
p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM);
|
||||
if (p == NULL) {
|
||||
MLD6_STATS_INC(mld6.memerr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Move to make room for Hop-by-hop options header. */
|
||||
if (pbuf_header(p, -IP6_HBH_HLEN)) {
|
||||
pbuf_free(p);
|
||||
MLD6_STATS_INC(mld6.lenerr);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Select our source address. */
|
||||
if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
|
||||
/* This is a special case, when we are performing duplicate address detection.
|
||||
* We must join the multicast group, but we don't have a valid address yet. */
|
||||
src_addr = IP6_ADDR_ANY6;
|
||||
} else {
|
||||
/* Use link-local address as source address. */
|
||||
src_addr = netif_ip6_addr(netif, 0);
|
||||
}
|
||||
|
||||
/* MLD message header pointer. */
|
||||
mld_hdr = (struct mld_header *)p->payload;
|
||||
|
||||
/* Set fields. */
|
||||
mld_hdr->type = type;
|
||||
mld_hdr->code = 0;
|
||||
mld_hdr->chksum = 0;
|
||||
mld_hdr->max_resp_delay = 0;
|
||||
mld_hdr->reserved = 0;
|
||||
ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address));
|
||||
|
||||
#if CHECKSUM_GEN_ICMP6
|
||||
IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) {
|
||||
mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
|
||||
src_addr, &(group->group_address));
|
||||
}
|
||||
#endif /* CHECKSUM_GEN_ICMP6 */
|
||||
|
||||
/* Add hop-by-hop headers options: router alert with MLD value. */
|
||||
ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD);
|
||||
|
||||
/* Send the packet out. */
|
||||
MLD6_STATS_INC(mld6.xmit);
|
||||
ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address),
|
||||
MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif);
|
||||
pbuf_free(p);
|
||||
}
|
||||
|
||||
#endif /* LWIP_IPV6 */
|
||||
1920
src/core/ipv6/nd6.c
Normal file
1920
src/core/ipv6/nd6.c
Normal file
File diff suppressed because it is too large
Load Diff
802
src/core/mem.c
802
src/core/mem.c
@@ -1,14 +1,30 @@
|
||||
/** @file
|
||||
*
|
||||
/**
|
||||
* @file
|
||||
* Dynamic memory manager
|
||||
*
|
||||
* This is a lightweight replacement for the standard C library malloc().
|
||||
*
|
||||
* If you want to use the standard C library malloc() instead, define
|
||||
* MEM_LIBC_MALLOC to 1 in your lwipopts.h
|
||||
*
|
||||
* To let mem_malloc() use pools (prevents fragmentation and is much faster than
|
||||
* a heap but might waste some memory), define MEM_USE_POOLS to 1, define
|
||||
* MEMP_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list
|
||||
* of pools like this (more pools can be added between _START and _END):
|
||||
*
|
||||
* Define three pools with sizes 256, 512, and 1512 bytes
|
||||
* LWIP_MALLOC_MEMPOOL_START
|
||||
* LWIP_MALLOC_MEMPOOL(20, 256)
|
||||
* LWIP_MALLOC_MEMPOOL(10, 512)
|
||||
* LWIP_MALLOC_MEMPOOL(5, 1512)
|
||||
* LWIP_MALLOC_MEMPOOL_END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
@@ -17,64 +33,309 @@
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* This file is part of the lwIP TCP/IP stack.
|
||||
*
|
||||
*
|
||||
* Author: Adam Dunkels <adam@sics.se>
|
||||
* Simon Goldschmidt
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "lwip/arch.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/mem.h"
|
||||
|
||||
#include "lwip/def.h"
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include "lwip/stats.h"
|
||||
#include "lwip/err.h"
|
||||
|
||||
struct mem {
|
||||
mem_size_t next, prev;
|
||||
#if MEM_ALIGNMENT == 1
|
||||
u8_t used;
|
||||
#elif MEM_ALIGNMENT == 2
|
||||
u16_t used;
|
||||
#elif MEM_ALIGNMENT == 4
|
||||
u32_t used;
|
||||
#else
|
||||
#error "unhandled MEM_ALIGNMENT size"
|
||||
#endif /* MEM_ALIGNMENT */
|
||||
};
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static struct mem *ram_end;
|
||||
static u8_t ram[MEM_SIZE + sizeof(struct mem) + MEM_ALIGNMENT];
|
||||
#if MEM_LIBC_MALLOC || MEM_USE_POOLS
|
||||
/** mem_init is not used when using pools instead of a heap or using
|
||||
* C library malloc().
|
||||
*/
|
||||
void
|
||||
mem_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
#define MIN_SIZE 12
|
||||
#if 0 /* this one does not align correctly for some, resulting in crashes */
|
||||
#define SIZEOF_STRUCT_MEM (unsigned int)MEM_ALIGN_SIZE(sizeof(struct mem))
|
||||
#else
|
||||
#define SIZEOF_STRUCT_MEM (sizeof(struct mem) + \
|
||||
(((sizeof(struct mem) % MEM_ALIGNMENT) == 0)? 0 : \
|
||||
(4 - (sizeof(struct mem) % MEM_ALIGNMENT))))
|
||||
/** mem_trim is not used when using pools instead of a heap or using
|
||||
* C library malloc(): we can't free part of a pool element and the stack
|
||||
* support mem_trim() to return a different pointer
|
||||
*/
|
||||
void*
|
||||
mem_trim(void *mem, mem_size_t size)
|
||||
{
|
||||
LWIP_UNUSED_ARG(size);
|
||||
return mem;
|
||||
}
|
||||
#endif /* MEM_LIBC_MALLOC || MEM_USE_POOLS */
|
||||
|
||||
#if MEM_LIBC_MALLOC
|
||||
/* lwIP heap implemented using C library malloc() */
|
||||
|
||||
/* in case C library malloc() needs extra protection,
|
||||
* allow these defines to be overridden.
|
||||
*/
|
||||
#ifndef mem_clib_free
|
||||
#define mem_clib_free free
|
||||
#endif
|
||||
#ifndef mem_clib_malloc
|
||||
#define mem_clib_malloc malloc
|
||||
#endif
|
||||
#ifndef mem_clib_calloc
|
||||
#define mem_clib_calloc calloc
|
||||
#endif
|
||||
|
||||
static struct mem *lfree; /* pointer to the lowest free block */
|
||||
#if LWIP_STATS && MEM_STATS
|
||||
#define MEM_LIBC_STATSHELPER_SIZE LWIP_MEM_ALIGN_SIZE(sizeof(mem_size_t))
|
||||
#else
|
||||
#define MEM_LIBC_STATSHELPER_SIZE 0
|
||||
#endif
|
||||
|
||||
static sys_sem_t mem_sem;
|
||||
/**
|
||||
* Allocate a block of memory with a minimum of 'size' bytes.
|
||||
*
|
||||
* @param size is the minimum size of the requested block in bytes.
|
||||
* @return pointer to allocated memory or NULL if no free memory was found.
|
||||
*
|
||||
* Note that the returned value must always be aligned (as defined by MEM_ALIGNMENT).
|
||||
*/
|
||||
void *
|
||||
mem_malloc(mem_size_t size)
|
||||
{
|
||||
void* ret = mem_clib_malloc(size + MEM_LIBC_STATSHELPER_SIZE);
|
||||
if (ret == NULL) {
|
||||
MEM_STATS_INC(err);
|
||||
} else {
|
||||
LWIP_ASSERT("malloc() must return aligned memory", LWIP_MEM_ALIGN(ret) == ret);
|
||||
#if LWIP_STATS && MEM_STATS
|
||||
*(mem_size_t*)ret = size;
|
||||
ret = (u8_t*)ret + MEM_LIBC_STATSHELPER_SIZE;
|
||||
MEM_STATS_INC_USED(used, size);
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Put memory back on the heap
|
||||
*
|
||||
* @param rmem is the pointer as returned by a previous call to mem_malloc()
|
||||
*/
|
||||
void
|
||||
mem_free(void *rmem)
|
||||
{
|
||||
LWIP_ASSERT("rmem != NULL", (rmem != NULL));
|
||||
LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
|
||||
#if LWIP_STATS && MEM_STATS
|
||||
rmem = (u8_t*)rmem - MEM_LIBC_STATSHELPER_SIZE;
|
||||
MEM_STATS_DEC_USED(used, *(mem_size_t*)rmem);
|
||||
#endif
|
||||
mem_clib_free(rmem);
|
||||
}
|
||||
|
||||
#elif MEM_USE_POOLS
|
||||
|
||||
/* lwIP heap implemented with different sized pools */
|
||||
|
||||
/**
|
||||
* Allocate memory: determine the smallest pool that is big enough
|
||||
* to contain an element of 'size' and get an element from that pool.
|
||||
*
|
||||
* @param size the size in bytes of the memory needed
|
||||
* @return a pointer to the allocated memory or NULL if the pool is empty
|
||||
*/
|
||||
void *
|
||||
mem_malloc(mem_size_t size)
|
||||
{
|
||||
void *ret;
|
||||
struct memp_malloc_helper *element;
|
||||
memp_t poolnr;
|
||||
mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
|
||||
|
||||
for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) {
|
||||
/* is this pool big enough to hold an element of the required size
|
||||
plus a struct memp_malloc_helper that saves the pool this element came from? */
|
||||
if (required_size <= memp_pools[poolnr]->size) {
|
||||
element = (struct memp_malloc_helper*)memp_malloc(poolnr);
|
||||
if (element == NULL) {
|
||||
/* No need to DEBUGF or ASSERT: This error is already taken care of in memp.c */
|
||||
#if MEM_USE_POOLS_TRY_BIGGER_POOL
|
||||
/** Try a bigger pool if this one is empty! */
|
||||
if (poolnr < MEMP_POOL_LAST) {
|
||||
continue;
|
||||
}
|
||||
#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
|
||||
MEM_STATS_INC(err);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (poolnr > MEMP_POOL_LAST) {
|
||||
LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
|
||||
MEM_STATS_INC(err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* save the pool number this element came from */
|
||||
element->poolnr = poolnr;
|
||||
/* and return a pointer to the memory directly after the struct memp_malloc_helper */
|
||||
ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
|
||||
|
||||
#if MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS)
|
||||
/* truncating to u16_t is safe because struct memp_desc::size is u16_t */
|
||||
element->size = (u16_t)size;
|
||||
MEM_STATS_INC_USED(used, element->size);
|
||||
#endif /* MEMP_OVERFLOW_CHECK || (LWIP_STATS && MEM_STATS) */
|
||||
#if MEMP_OVERFLOW_CHECK
|
||||
/* initialize unused memory (diff between requested size and selected pool's size) */
|
||||
memset((u8_t*)ret + size, 0xcd, memp_pools[poolnr]->size - size);
|
||||
#endif /* MEMP_OVERFLOW_CHECK */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free memory previously allocated by mem_malloc. Loads the pool number
|
||||
* and calls memp_free with that pool number to put the element back into
|
||||
* its pool
|
||||
*
|
||||
* @param rmem the memory element to free
|
||||
*/
|
||||
void
|
||||
mem_free(void *rmem)
|
||||
{
|
||||
struct memp_malloc_helper *hmem;
|
||||
|
||||
LWIP_ASSERT("rmem != NULL", (rmem != NULL));
|
||||
LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
|
||||
|
||||
/* get the original struct memp_malloc_helper */
|
||||
/* cast through void* to get rid of alignment warnings */
|
||||
hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)));
|
||||
|
||||
LWIP_ASSERT("hmem != NULL", (hmem != NULL));
|
||||
LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem)));
|
||||
LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX));
|
||||
|
||||
MEM_STATS_DEC_USED(used, hmem->size);
|
||||
#if MEMP_OVERFLOW_CHECK
|
||||
{
|
||||
u16_t i;
|
||||
LWIP_ASSERT("MEM_USE_POOLS: invalid chunk size",
|
||||
hmem->size <= memp_pools[hmem->poolnr]->size);
|
||||
/* check that unused memory remained untouched (diff between requested size and selected pool's size) */
|
||||
for (i = hmem->size; i < memp_pools[hmem->poolnr]->size; i++) {
|
||||
u8_t data = *((u8_t*)rmem + i);
|
||||
LWIP_ASSERT("MEM_USE_POOLS: mem overflow detected", data == 0xcd);
|
||||
}
|
||||
}
|
||||
#endif /* MEMP_OVERFLOW_CHECK */
|
||||
|
||||
/* and put it in the pool we saved earlier */
|
||||
memp_free(hmem->poolnr, hmem);
|
||||
}
|
||||
|
||||
#else /* MEM_USE_POOLS */
|
||||
/* lwIP replacement for your libc malloc() */
|
||||
|
||||
/**
|
||||
* The heap is made up as a list of structs of this type.
|
||||
* This does not have to be aligned since for getting its size,
|
||||
* we only use the macro SIZEOF_STRUCT_MEM, which automatically aligns.
|
||||
*/
|
||||
struct mem {
|
||||
/** index (-> ram[next]) of the next struct */
|
||||
mem_size_t next;
|
||||
/** index (-> ram[prev]) of the previous struct */
|
||||
mem_size_t prev;
|
||||
/** 1: this area is used; 0: this area is unused */
|
||||
u8_t used;
|
||||
};
|
||||
|
||||
/** All allocated blocks will be MIN_SIZE bytes big, at least!
|
||||
* MIN_SIZE can be overridden to suit your needs. Smaller values save space,
|
||||
* larger values could prevent too small blocks to fragment the RAM too much. */
|
||||
#ifndef MIN_SIZE
|
||||
#define MIN_SIZE 12
|
||||
#endif /* MIN_SIZE */
|
||||
/* some alignment macros: we define them here for better source code layout */
|
||||
#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE)
|
||||
#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))
|
||||
#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
|
||||
|
||||
/** If you want to relocate the heap to external memory, simply define
|
||||
* LWIP_RAM_HEAP_POINTER as a void-pointer to that location.
|
||||
* If so, make sure the memory at that location is big enough (see below on
|
||||
* how that space is calculated). */
|
||||
#ifndef LWIP_RAM_HEAP_POINTER
|
||||
/** the heap. we need one struct mem at the end and some room for alignment */
|
||||
LWIP_DECLARE_MEMORY_ALIGNED(ram_heap, MEM_SIZE_ALIGNED + (2U*SIZEOF_STRUCT_MEM));
|
||||
#define LWIP_RAM_HEAP_POINTER ram_heap
|
||||
#endif /* LWIP_RAM_HEAP_POINTER */
|
||||
|
||||
/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */
|
||||
static u8_t *ram;
|
||||
/** the last entry, always unused! */
|
||||
static struct mem *ram_end;
|
||||
/** pointer to the lowest free block, this is used for faster search */
|
||||
static struct mem *lfree;
|
||||
|
||||
/** concurrent access protection */
|
||||
#if !NO_SYS
|
||||
static sys_mutex_t mem_mutex;
|
||||
#endif
|
||||
|
||||
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
|
||||
|
||||
static volatile u8_t mem_free_count;
|
||||
|
||||
/* Allow mem_free from other (e.g. interrupt) context */
|
||||
#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free)
|
||||
#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free)
|
||||
#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free)
|
||||
#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc)
|
||||
#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc)
|
||||
#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc)
|
||||
|
||||
#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
|
||||
|
||||
/* Protect the heap only by using a semaphore */
|
||||
#define LWIP_MEM_FREE_DECL_PROTECT()
|
||||
#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex)
|
||||
#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex)
|
||||
/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */
|
||||
#define LWIP_MEM_ALLOC_DECL_PROTECT()
|
||||
#define LWIP_MEM_ALLOC_PROTECT()
|
||||
#define LWIP_MEM_ALLOC_UNPROTECT()
|
||||
|
||||
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
|
||||
|
||||
|
||||
/**
|
||||
* "Plug holes" by combining adjacent empty struct mems.
|
||||
* After this function is through, there should not exist
|
||||
* one empty struct mem pointing to another empty struct mem.
|
||||
*
|
||||
* @param mem this points to a struct mem which just has been freed
|
||||
* @internal this function is only called by mem_free() and mem_trim()
|
||||
*
|
||||
* This assumes access to the heap is protected by the calling function
|
||||
* already.
|
||||
*/
|
||||
static void
|
||||
plug_holes(struct mem *mem)
|
||||
{
|
||||
@@ -84,162 +345,265 @@ plug_holes(struct mem *mem)
|
||||
LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram);
|
||||
LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end);
|
||||
LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0);
|
||||
|
||||
|
||||
/* plug hole forward */
|
||||
LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE", mem->next <= MEM_SIZE);
|
||||
|
||||
nmem = (struct mem *)&ram[mem->next];
|
||||
LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED);
|
||||
|
||||
nmem = (struct mem *)(void *)&ram[mem->next];
|
||||
if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) {
|
||||
/* if mem->next is unused and not end of ram, combine mem and mem->next */
|
||||
if (lfree == nmem) {
|
||||
lfree = mem;
|
||||
}
|
||||
mem->next = nmem->next;
|
||||
((struct mem *)&ram[nmem->next])->prev = (u8_t *)mem - ram;
|
||||
((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram);
|
||||
}
|
||||
|
||||
/* plug hole backward */
|
||||
pmem = (struct mem *)&ram[mem->prev];
|
||||
pmem = (struct mem *)(void *)&ram[mem->prev];
|
||||
if (pmem != mem && pmem->used == 0) {
|
||||
/* if mem->prev is unused, combine mem and mem->prev */
|
||||
if (lfree == mem) {
|
||||
lfree = pmem;
|
||||
}
|
||||
pmem->next = mem->next;
|
||||
((struct mem *)&ram[mem->next])->prev = (u8_t *)pmem - ram;
|
||||
((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero the heap and initialize start, end and lowest-free
|
||||
*/
|
||||
void
|
||||
mem_init(void)
|
||||
{
|
||||
struct mem *mem;
|
||||
|
||||
memset(ram, 0, MEM_SIZE);
|
||||
mem = (struct mem *)ram;
|
||||
mem->next = MEM_SIZE;
|
||||
LWIP_ASSERT("Sanity check alignment",
|
||||
(SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0);
|
||||
|
||||
/* align the heap */
|
||||
ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
|
||||
/* initialize the start of the heap */
|
||||
mem = (struct mem *)(void *)ram;
|
||||
mem->next = MEM_SIZE_ALIGNED;
|
||||
mem->prev = 0;
|
||||
mem->used = 0;
|
||||
ram_end = (struct mem *)&ram[MEM_SIZE];
|
||||
/* initialize the end of the heap */
|
||||
ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED];
|
||||
ram_end->used = 1;
|
||||
ram_end->next = MEM_SIZE;
|
||||
ram_end->prev = MEM_SIZE;
|
||||
ram_end->next = MEM_SIZE_ALIGNED;
|
||||
ram_end->prev = MEM_SIZE_ALIGNED;
|
||||
|
||||
mem_sem = sys_sem_new(1);
|
||||
/* initialize the lowest-free pointer to the start of the heap */
|
||||
lfree = (struct mem *)(void *)ram;
|
||||
|
||||
lfree = (struct mem *)ram;
|
||||
MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
|
||||
|
||||
#ifdef MEM_STATS
|
||||
lwip_stats.mem.avail = MEM_SIZE;
|
||||
#endif /* MEM_STATS */
|
||||
if (sys_mutex_new(&mem_mutex) != ERR_OK) {
|
||||
LWIP_ASSERT("failed to create mem_mutex", 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a struct mem back on the heap
|
||||
*
|
||||
* @param rmem is the data portion of a struct mem as returned by a previous
|
||||
* call to mem_malloc()
|
||||
*/
|
||||
void
|
||||
mem_free(void *rmem)
|
||||
{
|
||||
struct mem *mem;
|
||||
LWIP_MEM_FREE_DECL_PROTECT();
|
||||
|
||||
if (rmem == NULL) {
|
||||
LWIP_DEBUGF(MEM_DEBUG | DBG_TRACE | 2, ("mem_free(p == NULL) was called.\n"));
|
||||
LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
sys_sem_wait(mem_sem);
|
||||
LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0);
|
||||
|
||||
LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
|
||||
(u8_t *)rmem < (u8_t *)ram_end);
|
||||
|
||||
|
||||
if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
|
||||
LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_free: illegal memory\n"));
|
||||
#ifdef MEM_STATS
|
||||
++lwip_stats.mem.err;
|
||||
#endif /* MEM_STATS */
|
||||
sys_sem_signal(mem_sem);
|
||||
SYS_ARCH_DECL_PROTECT(lev);
|
||||
LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
|
||||
/* protect mem stats from concurrent access */
|
||||
SYS_ARCH_PROTECT(lev);
|
||||
MEM_STATS_INC(illegal);
|
||||
SYS_ARCH_UNPROTECT(lev);
|
||||
return;
|
||||
}
|
||||
mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
|
||||
|
||||
/* protect the heap from concurrent access */
|
||||
LWIP_MEM_FREE_PROTECT();
|
||||
/* Get the corresponding struct mem ... */
|
||||
/* cast through void* to get rid of alignment warnings */
|
||||
mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
|
||||
/* ... which has to be in a used state ... */
|
||||
LWIP_ASSERT("mem_free: mem->used", mem->used);
|
||||
|
||||
/* ... and is now unused. */
|
||||
mem->used = 0;
|
||||
|
||||
if (mem < lfree) {
|
||||
/* the newly freed struct is now the lowest */
|
||||
lfree = mem;
|
||||
}
|
||||
|
||||
#ifdef MEM_STATS
|
||||
lwip_stats.mem.used -= mem->next - ((u8_t *)mem - ram);
|
||||
|
||||
#endif /* MEM_STATS */
|
||||
|
||||
MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram)));
|
||||
|
||||
/* finally, see if prev or next are free also */
|
||||
plug_holes(mem);
|
||||
sys_sem_signal(mem_sem);
|
||||
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
|
||||
mem_free_count = 1;
|
||||
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
|
||||
LWIP_MEM_FREE_UNPROTECT();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shrink memory returned by mem_malloc().
|
||||
*
|
||||
* @param rmem pointer to memory allocated by mem_malloc the is to be shrinked
|
||||
* @param newsize required size after shrinking (needs to be smaller than or
|
||||
* equal to the previous size)
|
||||
* @return for compatibility reasons: is always == rmem, at the moment
|
||||
* or NULL if newsize is > old size, in which case rmem is NOT touched
|
||||
* or freed!
|
||||
*/
|
||||
void *
|
||||
mem_reallocm(void *rmem, mem_size_t newsize)
|
||||
{
|
||||
void *nmem;
|
||||
nmem = mem_malloc(newsize);
|
||||
if (nmem == NULL) {
|
||||
return mem_realloc(rmem, newsize);
|
||||
}
|
||||
memcpy(nmem, rmem, newsize);
|
||||
mem_free(rmem);
|
||||
return nmem;
|
||||
}
|
||||
/*-----------------------------------------------------------------------------------*/
|
||||
void *
|
||||
mem_realloc(void *rmem, mem_size_t newsize)
|
||||
mem_trim(void *rmem, mem_size_t newsize)
|
||||
{
|
||||
mem_size_t size;
|
||||
mem_size_t ptr, ptr2;
|
||||
struct mem *mem, *mem2;
|
||||
/* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
|
||||
LWIP_MEM_FREE_DECL_PROTECT();
|
||||
|
||||
/* Expand the size of the allocated memory region so that we can
|
||||
adjust for alignment. */
|
||||
if ((newsize % MEM_ALIGNMENT) != 0) {
|
||||
newsize += MEM_ALIGNMENT - ((newsize + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT);
|
||||
newsize = LWIP_MEM_ALIGN_SIZE(newsize);
|
||||
|
||||
if (newsize < MIN_SIZE_ALIGNED) {
|
||||
/* every data block must be at least MIN_SIZE_ALIGNED long */
|
||||
newsize = MIN_SIZE_ALIGNED;
|
||||
}
|
||||
|
||||
if (newsize > MEM_SIZE) {
|
||||
|
||||
if (newsize > MEM_SIZE_ALIGNED) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sys_sem_wait(mem_sem);
|
||||
|
||||
LWIP_ASSERT("mem_realloc: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
|
||||
|
||||
LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
|
||||
(u8_t *)rmem < (u8_t *)ram_end);
|
||||
|
||||
|
||||
if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
|
||||
LWIP_DEBUGF(MEM_DEBUG | 3, ("mem_realloc: illegal memory\n"));
|
||||
SYS_ARCH_DECL_PROTECT(lev);
|
||||
LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n"));
|
||||
/* protect mem stats from concurrent access */
|
||||
SYS_ARCH_PROTECT(lev);
|
||||
MEM_STATS_INC(illegal);
|
||||
SYS_ARCH_UNPROTECT(lev);
|
||||
return rmem;
|
||||
}
|
||||
mem = (struct mem *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
|
||||
|
||||
ptr = (u8_t *)mem - ram;
|
||||
/* Get the corresponding struct mem ... */
|
||||
/* cast through void* to get rid of alignment warnings */
|
||||
mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
|
||||
/* ... and its offset pointer */
|
||||
ptr = (mem_size_t)((u8_t *)mem - ram);
|
||||
|
||||
size = mem->next - ptr - SIZEOF_STRUCT_MEM;
|
||||
#ifdef MEM_STATS
|
||||
lwip_stats.mem.used -= (size - newsize);
|
||||
#endif /* MEM_STATS */
|
||||
|
||||
if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) {
|
||||
LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
|
||||
if (newsize > size) {
|
||||
/* not supported */
|
||||
return NULL;
|
||||
}
|
||||
if (newsize == size) {
|
||||
/* No change in size, simply return */
|
||||
return rmem;
|
||||
}
|
||||
|
||||
/* protect the heap from concurrent access */
|
||||
LWIP_MEM_FREE_PROTECT();
|
||||
|
||||
mem2 = (struct mem *)(void *)&ram[mem->next];
|
||||
if (mem2->used == 0) {
|
||||
/* The next struct is unused, we can simply move it at little */
|
||||
mem_size_t next;
|
||||
/* remember the old next pointer */
|
||||
next = mem2->next;
|
||||
/* create new struct mem which is moved directly after the shrinked mem */
|
||||
ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
|
||||
mem2 = (struct mem *)&ram[ptr2];
|
||||
if (lfree == mem2) {
|
||||
lfree = (struct mem *)(void *)&ram[ptr2];
|
||||
}
|
||||
mem2 = (struct mem *)(void *)&ram[ptr2];
|
||||
mem2->used = 0;
|
||||
/* restore the next pointer */
|
||||
mem2->next = next;
|
||||
/* link it back to mem */
|
||||
mem2->prev = ptr;
|
||||
/* link mem to it */
|
||||
mem->next = ptr2;
|
||||
/* last thing to restore linked list: as we have moved mem2,
|
||||
* let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not
|
||||
* the end of the heap */
|
||||
if (mem2->next != MEM_SIZE_ALIGNED) {
|
||||
((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
|
||||
}
|
||||
MEM_STATS_DEC_USED(used, (size - newsize));
|
||||
/* no need to plug holes, we've already done that */
|
||||
} else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) {
|
||||
/* Next struct is used but there's room for another struct mem with
|
||||
* at least MIN_SIZE_ALIGNED of data.
|
||||
* Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem
|
||||
* ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED').
|
||||
* @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
|
||||
* region that couldn't hold data, but when mem->next gets freed,
|
||||
* the 2 regions would be combined, resulting in more free memory */
|
||||
ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
|
||||
mem2 = (struct mem *)(void *)&ram[ptr2];
|
||||
if (mem2 < lfree) {
|
||||
lfree = mem2;
|
||||
}
|
||||
mem2->used = 0;
|
||||
mem2->next = mem->next;
|
||||
mem2->prev = ptr;
|
||||
mem->next = ptr2;
|
||||
if (mem2->next != MEM_SIZE) {
|
||||
((struct mem *)&ram[mem2->next])->prev = ptr2;
|
||||
if (mem2->next != MEM_SIZE_ALIGNED) {
|
||||
((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
|
||||
}
|
||||
|
||||
plug_holes(mem2);
|
||||
MEM_STATS_DEC_USED(used, (size - newsize));
|
||||
/* the original mem->next is used, so no need to plug holes! */
|
||||
}
|
||||
sys_sem_signal(mem_sem);
|
||||
/* else {
|
||||
next struct mem is used but size between mem and mem2 is not big enough
|
||||
to create another struct mem
|
||||
-> don't do anyhting.
|
||||
-> the remaining space stays unused since it is too small
|
||||
} */
|
||||
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
|
||||
mem_free_count = 1;
|
||||
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
|
||||
LWIP_MEM_FREE_UNPROTECT();
|
||||
return rmem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a block of memory with a minimum of 'size' bytes.
|
||||
*
|
||||
* @param size is the minimum size of the requested block in bytes.
|
||||
* @return pointer to allocated memory or NULL if no free memory was found.
|
||||
*
|
||||
* Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
|
||||
*/
|
||||
void *
|
||||
mem_malloc(mem_size_t size)
|
||||
{
|
||||
mem_size_t ptr, ptr2;
|
||||
struct mem *mem, *mem2;
|
||||
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
|
||||
u8_t local_mem_free_count = 0;
|
||||
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
|
||||
LWIP_MEM_ALLOC_DECL_PROTECT();
|
||||
|
||||
if (size == 0) {
|
||||
return NULL;
|
||||
@@ -247,61 +611,163 @@ mem_malloc(mem_size_t size)
|
||||
|
||||
/* Expand the size of the allocated memory region so that we can
|
||||
adjust for alignment. */
|
||||
if ((size % MEM_ALIGNMENT) != 0) {
|
||||
size += MEM_ALIGNMENT - ((size + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT);
|
||||
size = LWIP_MEM_ALIGN_SIZE(size);
|
||||
|
||||
if (size < MIN_SIZE_ALIGNED) {
|
||||
/* every data block must be at least MIN_SIZE_ALIGNED long */
|
||||
size = MIN_SIZE_ALIGNED;
|
||||
}
|
||||
|
||||
if (size > MEM_SIZE) {
|
||||
|
||||
if (size > MEM_SIZE_ALIGNED) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sys_sem_wait(mem_sem);
|
||||
|
||||
for (ptr = (u8_t *)lfree - ram; ptr < MEM_SIZE; ptr = ((struct mem *)&ram[ptr])->next) {
|
||||
mem = (struct mem *)&ram[ptr];
|
||||
if (!mem->used &&
|
||||
mem->next - (ptr + SIZEOF_STRUCT_MEM) >= size + SIZEOF_STRUCT_MEM) {
|
||||
ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
|
||||
mem2 = (struct mem *)&ram[ptr2];
|
||||
/* protect the heap from concurrent access */
|
||||
sys_mutex_lock(&mem_mutex);
|
||||
LWIP_MEM_ALLOC_PROTECT();
|
||||
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
|
||||
/* run as long as a mem_free disturbed mem_malloc or mem_trim */
|
||||
do {
|
||||
local_mem_free_count = 0;
|
||||
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
|
||||
|
||||
mem2->prev = ptr;
|
||||
mem2->next = mem->next;
|
||||
mem->next = ptr2;
|
||||
if (mem2->next != MEM_SIZE) {
|
||||
((struct mem *)&ram[mem2->next])->prev = ptr2;
|
||||
/* Scan through the heap searching for a free block that is big enough,
|
||||
* beginning with the lowest free block.
|
||||
*/
|
||||
for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size;
|
||||
ptr = ((struct mem *)(void *)&ram[ptr])->next) {
|
||||
mem = (struct mem *)(void *)&ram[ptr];
|
||||
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
|
||||
mem_free_count = 0;
|
||||
LWIP_MEM_ALLOC_UNPROTECT();
|
||||
/* allow mem_free or mem_trim to run */
|
||||
LWIP_MEM_ALLOC_PROTECT();
|
||||
if (mem_free_count != 0) {
|
||||
/* If mem_free or mem_trim have run, we have to restart since they
|
||||
could have altered our current struct mem. */
|
||||
local_mem_free_count = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
mem2->used = 0;
|
||||
mem->used = 1;
|
||||
#ifdef MEM_STATS
|
||||
lwip_stats.mem.used += (size + SIZEOF_STRUCT_MEM);
|
||||
/* if (lwip_stats.mem.max < lwip_stats.mem.used) {
|
||||
lwip_stats.mem.max = lwip_stats.mem.used;
|
||||
} */
|
||||
if (lwip_stats.mem.max < ptr2) {
|
||||
lwip_stats.mem.max = ptr2;
|
||||
}
|
||||
#endif /* MEM_STATS */
|
||||
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
|
||||
|
||||
if (mem == lfree) {
|
||||
/* Find next free block after mem */
|
||||
while (lfree->used && lfree != ram_end) {
|
||||
lfree = (struct mem *)&ram[lfree->next];
|
||||
if ((!mem->used) &&
|
||||
(mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {
|
||||
/* mem is not used and at least perfect fit is possible:
|
||||
* mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
|
||||
|
||||
if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {
|
||||
/* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing
|
||||
* at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
|
||||
* -> split large block, create empty remainder,
|
||||
* remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
|
||||
* mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
|
||||
* struct mem would fit in but no data between mem2 and mem2->next
|
||||
* @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
|
||||
* region that couldn't hold data, but when mem->next gets freed,
|
||||
* the 2 regions would be combined, resulting in more free memory
|
||||
*/
|
||||
ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
|
||||
/* create mem2 struct */
|
||||
mem2 = (struct mem *)(void *)&ram[ptr2];
|
||||
mem2->used = 0;
|
||||
mem2->next = mem->next;
|
||||
mem2->prev = ptr;
|
||||
/* and insert it between mem and mem->next */
|
||||
mem->next = ptr2;
|
||||
mem->used = 1;
|
||||
|
||||
if (mem2->next != MEM_SIZE_ALIGNED) {
|
||||
((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
|
||||
}
|
||||
MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM));
|
||||
} else {
|
||||
/* (a mem2 struct does no fit into the user data space of mem and mem->next will always
|
||||
* be used at this point: if not we have 2 unused structs in a row, plug_holes should have
|
||||
* take care of this).
|
||||
* -> near fit or exact fit: do not split, no mem2 creation
|
||||
* also can't move mem->next directly behind mem, since mem->next
|
||||
* will always be used at this point!
|
||||
*/
|
||||
mem->used = 1;
|
||||
MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram));
|
||||
}
|
||||
LWIP_ASSERT("mem_malloc: !lfree->used", !lfree->used);
|
||||
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
|
||||
mem_malloc_adjust_lfree:
|
||||
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
|
||||
if (mem == lfree) {
|
||||
struct mem *cur = lfree;
|
||||
/* Find next free block after mem and update lowest free pointer */
|
||||
while (cur->used && cur != ram_end) {
|
||||
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
|
||||
mem_free_count = 0;
|
||||
LWIP_MEM_ALLOC_UNPROTECT();
|
||||
/* prevent high interrupt latency... */
|
||||
LWIP_MEM_ALLOC_PROTECT();
|
||||
if (mem_free_count != 0) {
|
||||
/* If mem_free or mem_trim have run, we have to restart since they
|
||||
could have altered our current struct mem or lfree. */
|
||||
goto mem_malloc_adjust_lfree;
|
||||
}
|
||||
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
|
||||
cur = (struct mem *)(void *)&ram[cur->next];
|
||||
}
|
||||
lfree = cur;
|
||||
LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
|
||||
}
|
||||
LWIP_MEM_ALLOC_UNPROTECT();
|
||||
sys_mutex_unlock(&mem_mutex);
|
||||
LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
|
||||
(mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end);
|
||||
LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
|
||||
((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
|
||||
LWIP_ASSERT("mem_malloc: sanity check alignment",
|
||||
(((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0);
|
||||
|
||||
return (u8_t *)mem + SIZEOF_STRUCT_MEM;
|
||||
}
|
||||
sys_sem_signal(mem_sem);
|
||||
LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
|
||||
(u32_t)mem + SIZEOF_STRUCT_MEM + size <= (u32_t)ram_end);
|
||||
LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
|
||||
(unsigned long)((u8_t *)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
|
||||
return (u8_t *)mem + SIZEOF_STRUCT_MEM;
|
||||
}
|
||||
}
|
||||
LWIP_DEBUGF(MEM_DEBUG | 2, ("mem_malloc: could not allocate %d bytes\n", (int)size));
|
||||
#ifdef MEM_STATS
|
||||
++lwip_stats.mem.err;
|
||||
#endif /* MEM_STATS */
|
||||
sys_sem_signal(mem_sem);
|
||||
}
|
||||
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
|
||||
/* if we got interrupted by a mem_free, try again */
|
||||
} while (local_mem_free_count != 0);
|
||||
#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
|
||||
LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
|
||||
MEM_STATS_INC(err);
|
||||
LWIP_MEM_ALLOC_UNPROTECT();
|
||||
sys_mutex_unlock(&mem_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* MEM_USE_POOLS */
|
||||
|
||||
#if MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS)
|
||||
void *
|
||||
mem_calloc(mem_size_t count, mem_size_t size)
|
||||
{
|
||||
return mem_clib_calloc(count, size);
|
||||
}
|
||||
|
||||
#else /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */
|
||||
/**
|
||||
* Contiguously allocates enough space for count objects that are size bytes
|
||||
* of memory each and returns a pointer to the allocated memory.
|
||||
*
|
||||
* The allocated memory is filled with bytes of value zero.
|
||||
*
|
||||
* @param count number of objects to allocate
|
||||
* @param size size of the objects to allocate
|
||||
* @return pointer to allocated memory / NULL pointer if there is an error
|
||||
*/
|
||||
void *
|
||||
mem_calloc(mem_size_t count, mem_size_t size)
|
||||
{
|
||||
void *p;
|
||||
|
||||
/* allocate 'count' objects of size 'size' */
|
||||
p = mem_malloc(count * size);
|
||||
if (p) {
|
||||
/* zero the memory */
|
||||
memset(p, 0, (size_t)count * (size_t)size);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
#endif /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user