diff options
author | Ulrich Drepper <drepper@redhat.com> | 2006-05-25 04:50:06 +0000 |
---|---|---|
committer | Ulrich Drepper <drepper@redhat.com> | 2006-05-25 04:50:06 +0000 |
commit | 07bfff20c78f861cb0cca78e4d26b9c662d21248 (patch) | |
tree | b4f0f0ab095d024f3f02dee0c8552e1b1b959ff0 /inet/inet6_opt.c | |
parent | aec6b246ee12824b8423885c9c445c869d86876e (diff) | |
download | glibc-07bfff20c78f861cb0cca78e4d26b9c662d21248.tar.gz glibc-07bfff20c78f861cb0cca78e4d26b9c662d21248.tar.xz glibc-07bfff20c78f861cb0cca78e4d26b9c662d21248.zip |
[BZ #2693]
* inet/Makefile (routines): Add inet6_opt and inet6_rth. * inet/Versions (libc, GLIBC_2.5): Add inet6_opt_init, inet6_opt_append, inet6_opt_finish, inet6_opt_set_val, inet6_opt_next, inet6_opt_find, inet6_opt_get_val, inet6_rth_space, inet6_rth_init, inet6_rth_add, inet6_rth_reverse, inet6_rth_segments, and inet6_rth_getaddr. * inet/netinet/ip6.h (struct ip6_rthdr0): Make ip6r0_addr a flexible array. * inet/netinet/in.h (struct ip6_mtuinfo): Define. Mark inet6_option_* interfaces as deprecated. Declare inet6_opt_init, inet6_opt_append, inet6_opt_finish, inet6_opt_set_val, inet6_opt_next, inet6_opt_find, inet6_opt_get_val, inet6_rth_space, inet6_rth_init, inet6_rth_add, inet6_rth_reverse, inet6_rth_segments, and inet6_rth_getaddr. * inet/inet6_opt.c: New file. * inet/inet6_rth.c: New file. * inet/netinet/icmp6.h: Pretty printing.
Diffstat (limited to 'inet/inet6_opt.c')
-rw-r--r-- | inet/inet6_opt.c | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/inet/inet6_opt.c b/inet/inet6_opt.c new file mode 100644 index 0000000000..bddb85182b --- /dev/null +++ b/inet/inet6_opt.c @@ -0,0 +1,278 @@ +/* Copyright (C) 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@redhat.com>, 2006. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <string.h> +#include <netinet/in.h> +#include <netinet/ip6.h> + + +/* RFC 3542, 10.1 + + This function returns the number of bytes needed for the empty + extension header i.e., without any options. If EXTBUF is not NULL it + also initializes the extension header to have the correct length + field. In that case if the EXTLEN value is not a positive (i.e., + non-zero) multiple of 8 the function fails and returns -1. */ +int +inet6_opt_init (void *extbuf, socklen_t extlen) +{ + if (extbuf != NULL) + { + if (extlen <= 0 || (extlen % 8) != 0) + return -1; + + /* Fill in the length in units of 8 octets. */ + struct ip6_hbh *extp = (struct ip6_hbh *) extbuf; + extp->ip6h_len = extlen / 8; + } + + return sizeof (struct ip6_hbh); +} + + +static void +add_padding (uint8_t *extbuf, int offset, int npad) +{ + if (npad == 1) + extbuf[offset] = IP6OPT_PAD1; + else + { + struct ip6_opt *pad_opt = (struct ip6_opt *) (extbuf + offset); + + pad_opt->ip6o_type = IP6OPT_PADN; + pad_opt->ip6o_len = npad - sizeof (struct ip6_opt); + /* Clear the memory used by the padding. */ + memset (pad_opt + 1, '\0', pad_opt->ip6o_len); + } +} + + + +/* RFC 3542, 10.2 + + This function returns the updated total length taking into account + adding an option with length 'len' and alignment 'align'. If + EXTBUF is not NULL then, in addition to returning the length, the + function inserts any needed pad option, initializes the option + (setting the type and length fields) and returns a pointer to the + location for the option content in databufp. If the option does + not fit in the extension header buffer the function returns -1. */ +int +inet6_opt_append (void *extbuf, socklen_t extlen, int offset, uint8_t type, + socklen_t len, uint8_t align, void **databufp) +{ + /* Check minimum offset. */ + if (offset < sizeof (struct ip6_hbh)) + return -1; + + /* One cannot add padding options. */ + if (type == IP6OPT_PAD1 || type == IP6OPT_PADN) + return -1; + + /* The option length must fit in one octet. */ + if (len > 255) + return -1; + + /* The alignment can only by 1, 2, 4, or 8 and must not exceed the + option length. */ + if (align == 0 || align > 8 || (align & (align - 1)) != 0 || align > len) + return -1; + + /* Determine the needed padding for alignment. Following the + current content of the buffer we have the is the IPv6 option type + and length, followed immediately by the data. The data has the + alignment constraints. Therefore padding must be inserted in the + form of padding options before the new option. */ + int data_offset = offset + sizeof (struct ip6_opt); + int npad = (align - data_offset % align) & (align - 1); + + /* Now we can check whether the buffer is large enough. */ + if (data_offset + npad + len > extlen) + return -1; + + if (npad != 0) + { + if (extbuf != NULL) + add_padding (extbuf, offset, npad); + + offset += npad; + } + + /* Now prepare the option itself. */ + if (extbuf != NULL) + { + struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset); + + opt->ip6o_type = type; + opt->ip6o_len = len; + + *databufp = opt + 1; + } + + return offset + sizeof (struct ip6_opt) + len; +} + + +/* RFC 3542, 10.3 + + This function returns the updated total length taking into account + the final padding of the extension header to make it a multiple of + 8 bytes. If EXTBUF is not NULL the function also initializes the + option by inserting a Pad1 or PadN option of the proper length. */ +int +inet6_opt_finish (void *extbuf, socklen_t extlen, int offset) +{ + /* Check minimum offset. */ + if (offset < sizeof (struct ip6_hbh)) + return -1; + + /* Required padding at the end. */ + int npad = (8 - (offset & 7)) & 7; + + /* Make sure the buffer is large enough. */ + if (offset + npad > extlen) + return -1; + + if (extbuf != NULL) + add_padding (extbuf, offset, npad); + + return offset + npad; +} + + +/* RFC 3542, 10.4 + + This function inserts data items of various sizes in the data + portion of the option. VAL should point to the data to be + inserted. OFFSET specifies where in the data portion of the option + the value should be inserted; the first byte after the option type + and length is accessed by specifying an offset of zero. */ +int +inet6_opt_set_val (void *databuf, int offset, void *val, socklen_t vallen) +{ + memcpy ((uint8_t *) databuf + offset, val, vallen); + + return offset + vallen; +} + + +/* RFC 3542, 10.5 + + This function parses received option extension headers returning + the next option. EXTBUF and EXTLEN specifies the extension header. + OFFSET should either be zero (for the first option) or the length + returned by a previous call to 'inet6_opt_next' or + 'inet6_opt_find'. It specifies the position where to continue + scanning the extension buffer. */ +int +inet6_opt_next (void *extbuf, socklen_t extlen, int offset, uint8_t *typep, + socklen_t *lenp, void **databufp) +{ + if (offset == 0) + offset = sizeof (struct ip6_hbh); + else if (offset < sizeof (struct ip6_hbh)) + return -1; + + while (offset < extlen) + { + struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset); + + if (opt->ip6o_type == IP6OPT_PAD1) + /* Single byte padding. */ + ++offset; + else if (opt->ip6o_type == IP6OPT_PADN) + offset += sizeof (struct ip6_opt) + opt->ip6o_len; + else + { + /* Check whether the option is valid. */ + offset += sizeof (struct ip6_opt) + opt->ip6o_len; + if (offset > extlen) + return -1; + + *typep = opt->ip6o_type; + *lenp = opt->ip6o_len; + *databufp = opt + 1; + return offset; + } + } + + return -1; +} + + +/* RFC 3542, 10.6 + + This function is similar to the previously described + 'inet6_opt_next' function, except this function lets the caller + specify the option type to be searched for, instead of always + returning the next option in the extension header. */ +int +inet6_opt_find (void *extbuf, socklen_t extlen, int offset, uint8_t type, + socklen_t *lenp, void **databufp) +{ + if (offset == 0) + offset = sizeof (struct ip6_hbh); + else if (offset < sizeof (struct ip6_hbh)) + return -1; + + while (offset < extlen) + { + struct ip6_opt *opt = (struct ip6_opt *) ((uint8_t *) extbuf + offset); + + if (opt->ip6o_type == IP6OPT_PAD1) + { + /* Single byte padding. */ + ++offset; + if (type == IP6OPT_PAD1) + { + *lenp = 0; + *databufp = (uint8_t *) extbuf + offset; + return offset; + } + } + else if (opt->ip6o_type != type) + offset += sizeof (struct ip6_opt) + opt->ip6o_len; + else + { + /* Check whether the option is valid. */ + offset += sizeof (struct ip6_opt) + opt->ip6o_len; + if (offset > extlen) + return -1; + + *lenp = opt->ip6o_len; + *databufp = opt + 1; + return offset; + } + } + + return -1; +} + + +/* RFC 3542, 10.7 + + This function extracts data items of various sizes in the data + portion of the option. */ +int +inet6_opt_get_val (void *databuf, int offset, void *val, socklen_t vallen) +{ + memcpy (val, (uint8_t *) databuf + offset, vallen); + + return offset + vallen; +} |