| 1 | /* Copyright (C) 2003-2020 Free Software Foundation, Inc. | 
|---|
| 2 | This file is part of the GNU C Library. | 
|---|
| 3 | Contributed by Ulrich Drepper <drepper@redhat.com>, 2003. | 
|---|
| 4 |  | 
|---|
| 5 | The GNU C Library is free software; you can redistribute it and/or | 
|---|
| 6 | modify it under the terms of the GNU Lesser General Public | 
|---|
| 7 | License as published by the Free Software Foundation; either | 
|---|
| 8 | version 2.1 of the License, or (at your option) any later version. | 
|---|
| 9 |  | 
|---|
| 10 | The GNU C Library is distributed in the hope that it will be useful, | 
|---|
| 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|---|
| 13 | Lesser General Public License for more details. | 
|---|
| 14 |  | 
|---|
| 15 | You should have received a copy of the GNU Lesser General Public | 
|---|
| 16 | License along with the GNU C Library; if not, see | 
|---|
| 17 | <https://www.gnu.org/licenses/>.  */ | 
|---|
| 18 |  | 
|---|
| 19 | #include <assert.h> | 
|---|
| 20 | #include <string.h> | 
|---|
| 21 | #include <netinet/in.h> | 
|---|
| 22 | #include <netinet/ip6.h> | 
|---|
| 23 | #include <sys/param.h> | 
|---|
| 24 |  | 
|---|
| 25 |  | 
|---|
| 26 | static void | 
|---|
| 27 | add_pad (struct cmsghdr *cmsg, int len) | 
|---|
| 28 | { | 
|---|
| 29 | unsigned char *p = CMSG_DATA (cmsg) + cmsg->cmsg_len - CMSG_LEN (0); | 
|---|
| 30 |  | 
|---|
| 31 | if (len == 1) | 
|---|
| 32 | /* Special handling for 1, a one-byte solution.  */ | 
|---|
| 33 | *p++ = IP6OPT_PAD1; | 
|---|
| 34 | else if (len != 0) | 
|---|
| 35 | { | 
|---|
| 36 | /* Multibyte padding.  */ | 
|---|
| 37 | *p++ = IP6OPT_PADN; | 
|---|
| 38 | *p++ = len - 2;	/* Discount the two header bytes.  */ | 
|---|
| 39 | /* The rest is filled with zero.  */ | 
|---|
| 40 | memset (p, '\0', len - 2); | 
|---|
| 41 | p += len - 2; | 
|---|
| 42 | } | 
|---|
| 43 |  | 
|---|
| 44 | /* Account for the bytes.  */ | 
|---|
| 45 | cmsg->cmsg_len += len; | 
|---|
| 46 | } | 
|---|
| 47 |  | 
|---|
| 48 |  | 
|---|
| 49 | static int | 
|---|
| 50 | get_opt_end (const uint8_t **result, const uint8_t *startp, | 
|---|
| 51 | const uint8_t *endp) | 
|---|
| 52 | { | 
|---|
| 53 | if (startp >= endp) | 
|---|
| 54 | /* Out of bounds.  */ | 
|---|
| 55 | return -1; | 
|---|
| 56 |  | 
|---|
| 57 | if (*startp == IP6OPT_PAD1) | 
|---|
| 58 | { | 
|---|
| 59 | /* Just this one byte.  */ | 
|---|
| 60 | *result = startp + 1; | 
|---|
| 61 | return 0; | 
|---|
| 62 | } | 
|---|
| 63 |  | 
|---|
| 64 | /* Now we know there must be at least two bytes.  */ | 
|---|
| 65 | if (startp + 2 > endp | 
|---|
| 66 | /* Now we can get the length byte.  */ | 
|---|
| 67 | || startp + startp[1] + 2 > endp) | 
|---|
| 68 | return -1; | 
|---|
| 69 |  | 
|---|
| 70 | *result = startp + startp[1] + 2; | 
|---|
| 71 |  | 
|---|
| 72 | return 0; | 
|---|
| 73 | } | 
|---|
| 74 |  | 
|---|
| 75 |  | 
|---|
| 76 | static uint8_t *option_alloc (struct cmsghdr *cmsg, int datalen, int multx, | 
|---|
| 77 | int plusy); | 
|---|
| 78 |  | 
|---|
| 79 |  | 
|---|
| 80 | /* RFC 2292, 6.3.1 | 
|---|
| 81 |  | 
|---|
| 82 | This function returns the number of bytes required to hold an option | 
|---|
| 83 | when it is stored as ancillary data, including the cmsghdr structure | 
|---|
| 84 | at the beginning, and any padding at the end (to make its size a | 
|---|
| 85 | multiple of 8 bytes).  The argument is the size of the structure | 
|---|
| 86 | defining the option, which must include any pad bytes at the | 
|---|
| 87 | beginning (the value y in the alignment term "xn + y"), the type | 
|---|
| 88 | byte, the length byte, and the option data.  */ | 
|---|
| 89 | int | 
|---|
| 90 | inet6_option_space (int nbytes) | 
|---|
| 91 | { | 
|---|
| 92 | /* Add room for the extension header.  */ | 
|---|
| 93 | nbytes += sizeof (struct ip6_ext); | 
|---|
| 94 |  | 
|---|
| 95 | return CMSG_SPACE (roundup (nbytes, 8)); | 
|---|
| 96 | } | 
|---|
| 97 | link_warning (inet6_option_space, | 
|---|
| 98 | "inet6_option_space is obsolete, use the RFC 3542 interfaces") | 
|---|
| 99 |  | 
|---|
| 100 |  | 
|---|
| 101 | /* RFC 2292, 6.3.2 | 
|---|
| 102 |  | 
|---|
| 103 | This function is called once per ancillary data object that will | 
|---|
| 104 | contain either Hop-by-Hop or Destination options.  It returns 0 on | 
|---|
| 105 | success or -1 on an error.  */ | 
|---|
| 106 | int | 
|---|
| 107 | inet6_option_init (void *bp, struct cmsghdr **cmsgp, int type) | 
|---|
| 108 | { | 
|---|
| 109 | /* Only Hop-by-Hop or Destination options allowed.  */ | 
|---|
| 110 | if (type != IPV6_HOPOPTS && type != IPV6_DSTOPTS) | 
|---|
| 111 | return -1; | 
|---|
| 112 |  | 
|---|
| 113 | /* BP is a pointer to the previously allocated space.  */ | 
|---|
| 114 | struct cmsghdr *newp = (struct cmsghdr *) bp; | 
|---|
| 115 |  | 
|---|
| 116 | /* Initialize the message header. | 
|---|
| 117 |  | 
|---|
| 118 | Length: No data yet, only the cmsghdr struct.  */ | 
|---|
| 119 | newp->cmsg_len = CMSG_LEN (0); | 
|---|
| 120 | /* Originating protocol: obviously IPv6.  */ | 
|---|
| 121 | newp->cmsg_level = IPPROTO_IPV6; | 
|---|
| 122 | /* Message type.  */ | 
|---|
| 123 | newp->cmsg_type = type; | 
|---|
| 124 |  | 
|---|
| 125 | /* Pass up the result.  */ | 
|---|
| 126 | *cmsgp = newp; | 
|---|
| 127 |  | 
|---|
| 128 | return 0; | 
|---|
| 129 | } | 
|---|
| 130 | link_warning (inet6_option_init, | 
|---|
| 131 | "inet6_option_init is obsolete, use the RFC 3542 interfaces") | 
|---|
| 132 |  | 
|---|
| 133 |  | 
|---|
| 134 | /* RFC 2292, 6.3.3 | 
|---|
| 135 |  | 
|---|
| 136 | This function appends a Hop-by-Hop option or a Destination option | 
|---|
| 137 | into an ancillary data object that has been initialized by | 
|---|
| 138 | inet6_option_init().  This function returns 0 if it succeeds or -1 on | 
|---|
| 139 | an error.  */ | 
|---|
| 140 | int | 
|---|
| 141 | inet6_option_append (struct cmsghdr *cmsg, const uint8_t *typep, int multx, | 
|---|
| 142 | int plusy) | 
|---|
| 143 | { | 
|---|
| 144 | /* typep is a pointer to the 8-bit option type.  It is assumed that this | 
|---|
| 145 | field is immediately followed by the 8-bit option data length field, | 
|---|
| 146 | which is then followed immediately by the option data. | 
|---|
| 147 |  | 
|---|
| 148 | The option types IP6OPT_PAD1 and IP6OPT_PADN also must be handled.  */ | 
|---|
| 149 | int len = typep[0] == IP6OPT_PAD1 ? 1 : typep[1] + 2; | 
|---|
| 150 |  | 
|---|
| 151 | /* Get the pointer to the space in the message.  */ | 
|---|
| 152 | uint8_t *ptr = option_alloc (cmsg, len, multx, plusy); | 
|---|
| 153 | if (ptr == NULL) | 
|---|
| 154 | /* Some problem with the parameters.  */ | 
|---|
| 155 | return -1; | 
|---|
| 156 |  | 
|---|
| 157 | /* Copy the content.  */ | 
|---|
| 158 | memcpy (ptr, typep, len); | 
|---|
| 159 |  | 
|---|
| 160 | return 0; | 
|---|
| 161 | } | 
|---|
| 162 | link_warning (inet6_option_append, | 
|---|
| 163 | "inet6_option_append is obsolete, use the RFC 3542 interfaces") | 
|---|
| 164 |  | 
|---|
| 165 |  | 
|---|
| 166 | /* RFC 2292, 6.3.4 | 
|---|
| 167 |  | 
|---|
| 168 | This function appends a Hop-by-Hop option or a Destination option | 
|---|
| 169 | into an ancillary data object that has been initialized by | 
|---|
| 170 | inet6_option_init().  This function returns a pointer to the 8-bit | 
|---|
| 171 | option type field that starts the option on success, or NULL on an | 
|---|
| 172 | error.  */ | 
|---|
| 173 | static uint8_t * | 
|---|
| 174 | option_alloc (struct cmsghdr *cmsg, int datalen, int multx, int plusy) | 
|---|
| 175 | { | 
|---|
| 176 | /* The RFC limits the value of the alignment values.  */ | 
|---|
| 177 | if ((multx != 1 && multx != 2 && multx != 4 && multx != 8) | 
|---|
| 178 | || ! (plusy >= 0 && plusy <= 7)) | 
|---|
| 179 | return NULL; | 
|---|
| 180 |  | 
|---|
| 181 | /* Current data size.  */ | 
|---|
| 182 | int dsize = cmsg->cmsg_len - CMSG_LEN (0); | 
|---|
| 183 |  | 
|---|
| 184 | /* The first two bytes of the option are for the extended header.  */ | 
|---|
| 185 | if (__glibc_unlikely (dsize == 0)) | 
|---|
| 186 | { | 
|---|
| 187 | cmsg->cmsg_len += sizeof (struct ip6_ext); | 
|---|
| 188 | dsize = sizeof (struct ip6_ext); | 
|---|
| 189 | } | 
|---|
| 190 |  | 
|---|
| 191 | /* First add padding.  */ | 
|---|
| 192 | add_pad (cmsg, ((multx - (dsize & (multx - 1))) & (multx - 1)) + plusy); | 
|---|
| 193 |  | 
|---|
| 194 | /* Return the pointer to the start of the option space.  */ | 
|---|
| 195 | uint8_t *result = CMSG_DATA (cmsg) + cmsg->cmsg_len - CMSG_LEN (0); | 
|---|
| 196 | cmsg->cmsg_len += datalen; | 
|---|
| 197 |  | 
|---|
| 198 | /* The extended option header length is measured in 8-byte groups. | 
|---|
| 199 | To represent the current length we might have to add padding.  */ | 
|---|
| 200 | dsize = cmsg->cmsg_len - CMSG_LEN (0); | 
|---|
| 201 | add_pad (cmsg, (8 - (dsize & (8 - 1))) & (8 - 1)); | 
|---|
| 202 |  | 
|---|
| 203 | /* Record the new length of the option.  */ | 
|---|
| 204 | assert (((cmsg->cmsg_len - CMSG_LEN (0)) % 8) == 0); | 
|---|
| 205 | int len8b = (cmsg->cmsg_len - CMSG_LEN (0)) / 8 - 1; | 
|---|
| 206 | if (len8b >= 256) | 
|---|
| 207 | /* Too long.  */ | 
|---|
| 208 | return NULL; | 
|---|
| 209 |  | 
|---|
| 210 | struct ip6_ext *ie = (void *) CMSG_DATA (cmsg); | 
|---|
| 211 | ie->ip6e_len = len8b; | 
|---|
| 212 |  | 
|---|
| 213 | return result; | 
|---|
| 214 | } | 
|---|
| 215 |  | 
|---|
| 216 |  | 
|---|
| 217 | uint8_t * | 
|---|
| 218 | inet6_option_alloc (struct cmsghdr *cmsg, int datalen, int multx, int plusy) | 
|---|
| 219 | { | 
|---|
| 220 | return option_alloc (cmsg, datalen, multx, plusy); | 
|---|
| 221 | } | 
|---|
| 222 | link_warning (inet6_option_alloc, | 
|---|
| 223 | "inet6_option_alloc is obsolete, use the RFC 3542 interfaces") | 
|---|
| 224 |  | 
|---|
| 225 |  | 
|---|
| 226 | /* RFC 2292, 6.3.5 | 
|---|
| 227 |  | 
|---|
| 228 | This function processes the next Hop-by-Hop option or Destination | 
|---|
| 229 | option in an ancillary data object.  If another option remains to be | 
|---|
| 230 | processed, the return value of the function is 0 and *tptrp points to | 
|---|
| 231 | the 8-bit option type field (which is followed by the 8-bit option | 
|---|
| 232 | data length, followed by the option data).  If no more options remain | 
|---|
| 233 | to be processed, the return value is -1 and *tptrp is NULL.  If an | 
|---|
| 234 | error occurs, the return value is -1 and *tptrp is not NULL.  */ | 
|---|
| 235 | int | 
|---|
| 236 | inet6_option_next (const struct cmsghdr *cmsg, uint8_t **tptrp) | 
|---|
| 237 | { | 
|---|
| 238 | /* Make sure it is an option of the right type.  */ | 
|---|
| 239 | if (cmsg->cmsg_level != IPPROTO_IPV6 | 
|---|
| 240 | || (cmsg->cmsg_type != IPV6_HOPOPTS && cmsg->cmsg_type != IPV6_DSTOPTS)) | 
|---|
| 241 | return -1; | 
|---|
| 242 |  | 
|---|
| 243 | /* Pointer to the extension header.  We only compute the address, we | 
|---|
| 244 | don't access anything yet.  */ | 
|---|
| 245 | const struct ip6_ext *ip6e = (const struct ip6_ext *) CMSG_DATA (cmsg); | 
|---|
| 246 |  | 
|---|
| 247 | /* Make sure the message is long enough.  */ | 
|---|
| 248 | if (cmsg->cmsg_len < CMSG_LEN (sizeof (struct ip6_ext)) | 
|---|
| 249 | /* Now we can access the extension header.  */ | 
|---|
| 250 | || cmsg->cmsg_len < CMSG_LEN ((ip6e->ip6e_len + 1) * 8)) | 
|---|
| 251 | /* Too small.  */ | 
|---|
| 252 | return -1; | 
|---|
| 253 |  | 
|---|
| 254 | /* Determine the address of the byte past the message.  */ | 
|---|
| 255 | const uint8_t *endp = CMSG_DATA (cmsg) + (ip6e->ip6e_len + 1) * 8; | 
|---|
| 256 |  | 
|---|
| 257 | const uint8_t *result; | 
|---|
| 258 | if (*tptrp == NULL) | 
|---|
| 259 | /* This is the first call, return the first option if there is one.  */ | 
|---|
| 260 | result = (const uint8_t *) (ip6e + 1); | 
|---|
| 261 | else | 
|---|
| 262 | { | 
|---|
| 263 | /* Make sure *TPTRP points to a beginning of a new option in | 
|---|
| 264 | the message.  The upper limit is checked in get_opt_end.  */ | 
|---|
| 265 | if (*tptrp < (const uint8_t *) (ip6e + 1)) | 
|---|
| 266 | return -1; | 
|---|
| 267 |  | 
|---|
| 268 | /* Get the beginning of the next option.  */ | 
|---|
| 269 | if (get_opt_end (&result, *tptrp, endp) != 0) | 
|---|
| 270 | return -1; | 
|---|
| 271 | } | 
|---|
| 272 |  | 
|---|
| 273 | /* We know where the next option starts.  */ | 
|---|
| 274 | *tptrp = (uint8_t *) result; | 
|---|
| 275 |  | 
|---|
| 276 | /* Check the option is fully represented in the message.  */ | 
|---|
| 277 | return get_opt_end (&result, result, endp); | 
|---|
| 278 | } | 
|---|
| 279 | link_warning (inet6_option_next, | 
|---|
| 280 | "inet6_option_next is obsolete, use the RFC 3542 interfaces") | 
|---|
| 281 |  | 
|---|
| 282 |  | 
|---|
| 283 | /* RFC 2292, 6.3.6 | 
|---|
| 284 |  | 
|---|
| 285 | This function is similar to the previously described | 
|---|
| 286 | inet6_option_next() function, except this function lets the caller | 
|---|
| 287 | specify the option type to be searched for, instead of always | 
|---|
| 288 | returning the next option in the ancillary data object.  cmsg is a | 
|---|
| 289 | pointer to cmsghdr structure of which cmsg_level equals IPPROTO_IPV6 | 
|---|
| 290 | and cmsg_type equals either IPV6_HOPOPTS or IPV6_DSTOPTS.  */ | 
|---|
| 291 | int | 
|---|
| 292 | inet6_option_find (const struct cmsghdr *cmsg, uint8_t **tptrp, int type) | 
|---|
| 293 | { | 
|---|
| 294 | /* Make sure it is an option of the right type.  */ | 
|---|
| 295 | if (cmsg->cmsg_level != IPPROTO_IPV6 | 
|---|
| 296 | || (cmsg->cmsg_type != IPV6_HOPOPTS && cmsg->cmsg_type != IPV6_DSTOPTS)) | 
|---|
| 297 | return -1; | 
|---|
| 298 |  | 
|---|
| 299 | /* Pointer to the extension header.  We only compute the address, we | 
|---|
| 300 | don't access anything yet.  */ | 
|---|
| 301 | const struct ip6_ext *ip6e = (const struct ip6_ext *) CMSG_DATA (cmsg); | 
|---|
| 302 |  | 
|---|
| 303 | /* Make sure the message is long enough.  */ | 
|---|
| 304 | if (cmsg->cmsg_len < CMSG_LEN (sizeof (struct ip6_ext)) | 
|---|
| 305 | /* Now we can access the extension header.  */ | 
|---|
| 306 | || cmsg->cmsg_len < CMSG_LEN ((ip6e->ip6e_len + 1) * 8)) | 
|---|
| 307 | /* Too small.  */ | 
|---|
| 308 | return -1; | 
|---|
| 309 |  | 
|---|
| 310 | /* Determine the address of the byte past the message.  */ | 
|---|
| 311 | const uint8_t *endp = CMSG_DATA (cmsg) + (ip6e->ip6e_len + 1) * 8; | 
|---|
| 312 |  | 
|---|
| 313 | const uint8_t *next; | 
|---|
| 314 | if (*tptrp == NULL) | 
|---|
| 315 | /* This is the first call, return the first option if there is one.  */ | 
|---|
| 316 | next = (const uint8_t *) (ip6e + 1); | 
|---|
| 317 | else | 
|---|
| 318 | { | 
|---|
| 319 | /* Make sure *TPTRP points to a beginning of a new option in | 
|---|
| 320 | the message.  The upper limit is checked in get_opt_end.  */ | 
|---|
| 321 | if (*tptrp < (const uint8_t *) (ip6e + 1)) | 
|---|
| 322 | return -1; | 
|---|
| 323 |  | 
|---|
| 324 | /* Get the beginning of the next option.  */ | 
|---|
| 325 | if (get_opt_end (&next, *tptrp, endp) != 0) | 
|---|
| 326 | return -1; | 
|---|
| 327 | } | 
|---|
| 328 |  | 
|---|
| 329 | /* Now search for the appropriate typed entry.  */ | 
|---|
| 330 | const uint8_t *result; | 
|---|
| 331 | do | 
|---|
| 332 | { | 
|---|
| 333 | result = next; | 
|---|
| 334 |  | 
|---|
| 335 | /* Get the end of this entry.  */ | 
|---|
| 336 | if (get_opt_end (&next, result, endp) != 0) | 
|---|
| 337 | return -1; | 
|---|
| 338 | } | 
|---|
| 339 | while (*result != type); | 
|---|
| 340 |  | 
|---|
| 341 | /* We know where the next option starts.  */ | 
|---|
| 342 | *tptrp = (uint8_t *) result; | 
|---|
| 343 |  | 
|---|
| 344 | /* Success.  */ | 
|---|
| 345 | return 0; | 
|---|
| 346 | } | 
|---|
| 347 | link_warning (inet6_option_find, | 
|---|
| 348 | "inet6_option_find is obsolete, use the RFC 3542 interfaces") | 
|---|
| 349 |  | 
|---|