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