1 /* $Id: ethernet.c,v 1.4 2006/11/15 22:17:30 fredette Exp $ */
2 
3 /* generic/ethernet.c - generic ethernet implementation support: */
4 
5 /*
6  * Copyright (c) 2003 Matt Fredette
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by Matt Fredette.
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <tme/common.h>
37 _TME_RCSID("$Id: ethernet.c,v 1.4 2006/11/15 22:17:30 fredette Exp $");
38 
39 /* includes: */
40 #include <tme/generic/ethernet.h>
41 #include <stdlib.h>
42 #include <errno.h>
43 
44 /* the Ethernet broadcast address: */
45 const tme_uint8_t tme_ethernet_addr_broadcast[TME_ETHERNET_ADDR_SIZE] = {
46   0xff, 0xff, 0xff, 0xff, 0xff, 0xff
47 };
48 
49 /* this scores an Ethernet connection: */
50 int
tme_ethernet_connection_score(struct tme_connection * conn,unsigned int * _score)51 tme_ethernet_connection_score(struct tme_connection *conn, unsigned int *_score)
52 {
53   /* both sides must be Ethernet connections: */
54   assert(conn->tme_connection_type == TME_CONNECTION_ETHERNET);
55   assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_ETHERNET);
56 
57   /* XXX we don't do any real checking: */
58   *_score = 1;
59   return (TME_OK);
60 }
61 
62 /* this parses an ethernet address: */
63 int
tme_ethernet_addr_parse(const char * addr_string,tme_uint8_t * addr_bytes)64 tme_ethernet_addr_parse(const char *addr_string, tme_uint8_t *addr_bytes)
65 {
66   const char *p1;
67   char *p2;
68   unsigned long byte;
69   int byte_i;
70 
71   /* if we were given a NULL string, the parse fails: */
72   if (addr_string == NULL) {
73     return (EINVAL);
74   }
75 
76   /* loop converting bytes: */
77   p1 = addr_string;
78   byte_i = 0;
79   for(;;) {
80 
81     /* convert the next byte: */
82     byte = strtoul(p1, &p2, 16);
83 
84     /* if some characters were converted: */
85     if (p2 != p1) {
86 
87       /* if this byte is out of range, the parse fails: */
88       if (byte > 0xff) {
89 	return (EINVAL);
90       }
91 
92       /* if this is one too many bytes for an Ethernet address, the parse fails: */
93       else if (byte_i == TME_ETHERNET_ADDR_SIZE) {
94 	return (EINVAL);
95       }
96 
97       /* store this byte: */
98       addr_bytes[byte_i++] = byte;
99     }
100 
101     /* if the conversion stopped on a NUL: */
102     if (*p2 == '\0') {
103 
104       /* if we haven't converted enough bytes, the parse fails,
105 	 otherwise the parse succeeds: */
106       return ((byte_i == TME_ETHERNET_ADDR_SIZE)
107 	      ? TME_OK
108 	      : EINVAL);
109     }
110 
111     /* if the conversion stopped on a colon, skip the colon and continue: */
112     else if (*p2 == ':') {
113       p1 = p2 + 1;
114     }
115 
116     /* otherwise, the parse fails: */
117     else {
118       return (EINVAL);
119     }
120   }
121   /* NOTREACHED */
122 }
123 
124 /* this copies frame chunks: */
125 unsigned int
tme_ethernet_chunks_copy(const struct tme_ethernet_frame_chunk * chunks_dst,const struct tme_ethernet_frame_chunk * chunks_src)126 tme_ethernet_chunks_copy(const struct tme_ethernet_frame_chunk *chunks_dst,
127 			 const struct tme_ethernet_frame_chunk *chunks_src)
128 {
129   const struct tme_ethernet_frame_chunk *chunk_dst;
130   const struct tme_ethernet_frame_chunk *chunk_src;
131   tme_uint8_t *chunk_bytes_dst;
132   const tme_uint8_t *chunk_bytes_src;
133   unsigned int chunk_size_dst;
134   unsigned int chunk_size_src;
135   unsigned int chunk_size;
136   unsigned int frame_size_total;
137 
138   chunk_src = chunks_src;
139   chunk_bytes_src = chunk_src->tme_ethernet_frame_chunk_bytes;
140   chunk_size_src = chunk_src->tme_ethernet_frame_chunk_bytes_count;
141 
142   frame_size_total = 0;
143 
144   /* if we have been given a destination: */
145   if (chunks_dst != NULL) {
146 
147     chunk_dst = chunks_dst;
148     chunk_bytes_dst = chunk_dst->tme_ethernet_frame_chunk_bytes;
149     chunk_size_dst = chunk_dst->tme_ethernet_frame_chunk_bytes_count;
150 
151     /* copy until we run out of source or destination chunks: */
152     for (; (chunk_dst != NULL
153 	    && chunk_src != NULL); ) {
154 
155       /* get the amount we can copy now: */
156       chunk_size = TME_MIN(chunk_size_dst, chunk_size_src);
157       assert(chunk_size > 0);
158 
159       /* copy: */
160       memcpy(chunk_bytes_dst, chunk_bytes_src, chunk_size);
161 
162       /* update: */
163       frame_size_total += chunk_size;
164       chunk_bytes_src += chunk_size;
165       chunk_size_src -= chunk_size;
166       if (chunk_size_src == 0
167 	  && (chunk_src = chunk_src->tme_ethernet_frame_chunk_next) != NULL) {
168 	chunk_bytes_src = chunk_src->tme_ethernet_frame_chunk_bytes;
169 	chunk_size_src = chunk_src->tme_ethernet_frame_chunk_bytes_count;
170       }
171       chunk_bytes_dst += chunk_size;
172       chunk_size_dst -= chunk_size;
173       if (chunk_size_dst == 0
174 	  && (chunk_dst = chunk_dst->tme_ethernet_frame_chunk_next) != NULL) {
175 	chunk_bytes_dst = chunk_dst->tme_ethernet_frame_chunk_bytes;
176 	chunk_size_dst = chunk_dst->tme_ethernet_frame_chunk_bytes_count;
177       }
178     }
179   }
180 
181   /* count up the uncopied bytes: */
182   for (; chunk_src != NULL; ) {
183     frame_size_total += chunk_size_src;
184     if ((chunk_src = chunk_src->tme_ethernet_frame_chunk_next) != NULL) {
185       chunk_size_src = chunk_src->tme_ethernet_frame_chunk_bytes_count;
186     }
187   }
188 
189   /* done: */
190   return (frame_size_total);
191 }
192 
193 /* this calculates a little-endian Ethernet CRC.  this was cribbed
194    from NetBSD's src/sys/net/if_ethersubr.c: */
195 tme_uint32_t
tme_ethernet_crc32_el(const tme_uint8_t * buf,unsigned int len)196 tme_ethernet_crc32_el(const tme_uint8_t *buf, unsigned int len)
197 {
198   static const tme_uint32_t crctab[] = {
199     0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
200     0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
201     0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
202     0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
203   };
204   tme_uint32_t crc;
205   unsigned int i;
206 
207   crc = ((tme_uint32_t) 0) - 1;	/* initial value */
208 
209   for (i = 0; i < len; i++) {
210     crc ^= buf[i];
211     crc = (crc >> 4) ^ crctab[crc & 0xf];
212     crc = (crc >> 4) ^ crctab[crc & 0xf];
213   }
214 
215   return (crc);
216 }
217