win-pvdrivers

annotate xennet/xennet_common.c @ 615:0436238bcda5

Fix a crash when an incorrectly formatted TCP packet is sent/forwarded by Dom0
author James Harper <james.harper@bendigoit.com.au>
date Sat Jul 18 09:24:52 2009 +1000 (2009-07-18)
parents 214866b0a8fd
children 31c56358c9fc
rev   line source
james@246 1 /*
james@246 2 PV Net Driver for Windows Xen HVM Domains
james@246 3 Copyright (C) 2007 James Harper
james@246 4 Copyright (C) 2007 Andrew Grover <andy.grover@oracle.com>
james@246 5
james@246 6 This program is free software; you can redistribute it and/or
james@246 7 modify it under the terms of the GNU General Public License
james@246 8 as published by the Free Software Foundation; either version 2
james@246 9 of the License, or (at your option) any later version.
james@246 10
james@246 11 This program is distributed in the hope that it will be useful,
james@246 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
james@246 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
james@246 14 GNU General Public License for more details.
james@246 15
james@246 16 You should have received a copy of the GNU General Public License
james@246 17 along with this program; if not, write to the Free Software
james@246 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
james@246 19 */
james@246 20
james@246 21 #include "xennet.h"
james@246 22
james@532 23 /*
james@532 24 Increase the header to a certain size
james@532 25 */
james@532 26
james@532 27 BOOLEAN
james@532 28 XenNet_BuildHeader(packet_info_t *pi, ULONG new_header_size)
james@532 29 {
james@532 30 ULONG bytes_remaining;
james@532 31
james@532 32 //FUNCTION_ENTER();
james@532 33
james@532 34 if (new_header_size <= pi->header_length)
james@532 35 {
james@596 36 return TRUE; /* header is already at least the required size */
james@532 37 }
james@532 38
james@596 39 if (pi->header == pi->first_buffer_virtual)
james@532 40 {
james@596 41 /* still working in the first buffer */
james@596 42 if (new_header_size <= pi->first_buffer_length)
james@536 43 {
james@596 44 //KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ " new_header_size <= pi->first_buffer_length\n"));
james@596 45 pi->header_length = new_header_size;
james@596 46 if (pi->header_length == pi->first_buffer_length)
james@596 47 {
james@596 48 NdisGetNextBuffer(pi->curr_buffer, &pi->curr_buffer);
james@596 49 pi->curr_mdl_offset = 0;
james@596 50 }
james@596 51 else
james@596 52 {
james@596 53 pi->curr_mdl_offset = (USHORT)new_header_size;
james@596 54 if (pi->curr_pb)
james@596 55 pi->curr_pb = pi->curr_pb->next;
james@596 56 }
james@596 57 return TRUE;
james@536 58 }
james@536 59 else
james@536 60 {
james@596 61 //KdPrint((__DRIVER_NAME " Switching to header_data\n"));
james@596 62 memcpy(pi->header_data, pi->header, pi->header_length);
james@596 63 pi->header = pi->header_data;
james@536 64 }
james@532 65 }
james@532 66
james@532 67 bytes_remaining = new_header_size - pi->header_length;
james@532 68
james@536 69 //KdPrint((__DRIVER_NAME " A bytes_remaining = %d, pi->curr_buffer = %p, pi->mdl_count = %d\n", bytes_remaining, pi->curr_buffer, pi->mdl_count));
james@536 70 while (bytes_remaining && pi->curr_buffer)
james@532 71 {
james@532 72 ULONG copy_size;
james@532 73
james@536 74 ASSERT(pi->curr_buffer);
james@536 75 //KdPrint((__DRIVER_NAME " B bytes_remaining = %d, pi->curr_buffer = %p, pi->mdl_count = %d\n", bytes_remaining, pi->curr_buffer, pi->mdl_count));
james@536 76 if (MmGetMdlByteCount(pi->curr_buffer))
james@532 77 {
james@554 78 PUCHAR src_addr;
james@554 79 src_addr = MmGetSystemAddressForMdlSafe(pi->curr_buffer, NormalPagePriority);
james@554 80 if (!src_addr)
james@554 81 return FALSE;
james@536 82 copy_size = min(bytes_remaining, MmGetMdlByteCount(pi->curr_buffer) - pi->curr_mdl_offset);
james@532 83 //KdPrint((__DRIVER_NAME " B copy_size = %d\n", copy_size));
james@532 84 memcpy(pi->header + pi->header_length,
james@554 85 src_addr + pi->curr_mdl_offset, copy_size);
james@536 86 pi->curr_mdl_offset = (USHORT)(pi->curr_mdl_offset + copy_size);
james@532 87 pi->header_length += copy_size;
james@532 88 bytes_remaining -= copy_size;
james@532 89 }
james@536 90 if (pi->curr_mdl_offset == MmGetMdlByteCount(pi->curr_buffer))
james@532 91 {
james@536 92 NdisGetNextBuffer(pi->curr_buffer, &pi->curr_buffer);
james@536 93 if (pi->curr_pb)
james@536 94 pi->curr_pb = pi->curr_pb->next;
james@532 95 pi->curr_mdl_offset = 0;
james@532 96 }
james@532 97 }
james@536 98 //KdPrint((__DRIVER_NAME " C bytes_remaining = %d, pi->curr_buffer = %p, pi->mdl_count = %d\n", bytes_remaining, pi->curr_buffer, pi->mdl_count));
james@532 99 if (bytes_remaining)
james@532 100 {
james@532 101 //KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ " bytes_remaining\n"));
james@532 102 return FALSE;
james@532 103 }
james@532 104 //FUNCTION_EXIT();
james@532 105 return TRUE;
james@532 106 }
james@532 107
james@246 108 ULONG
james@596 109 XenNet_ParsePacketHeader(packet_info_t *pi, PUCHAR alt_buffer, ULONG min_header_size)
james@246 110 {
james@532 111 //FUNCTION_ENTER();
james@246 112
james@536 113 ASSERT(pi->first_buffer);
james@246 114
james@596 115 NdisQueryBufferSafe(pi->first_buffer, (PVOID)&pi->first_buffer_virtual, &pi->first_buffer_length, NormalPagePriority);
james@536 116 pi->curr_buffer = pi->first_buffer;
james@596 117 if (alt_buffer)
james@596 118 pi->header = alt_buffer;
james@596 119 else
james@596 120 pi->header = pi->first_buffer_virtual;
james@246 121
james@532 122 pi->header_length = 0;
james@532 123 pi->curr_mdl_offset = 0;
james@532 124
james@596 125 if (!XenNet_BuildHeader(pi, max((ULONG)XN_HDR_SIZE, min_header_size)))
james@246 126 {
james@532 127 KdPrint((__DRIVER_NAME " packet too small (Ethernet Header)\n"));
james@246 128 return PARSE_TOO_SMALL;
james@246 129 }
james@532 130
james@422 131 switch (GET_NET_PUSHORT(&pi->header[12])) // L2 protocol field
james@246 132 {
james@246 133 case 0x0800:
james@532 134 //KdPrint((__DRIVER_NAME " IP\n"));
james@532 135 if (pi->header_length < (ULONG)(XN_HDR_SIZE + 20))
james@532 136 {
james@532 137 if (!XenNet_BuildHeader(pi, (ULONG)(XN_HDR_SIZE + 20)))
james@532 138 {
james@532 139 KdPrint((__DRIVER_NAME " packet too small (IP Header)\n"));
james@532 140 return PARSE_TOO_SMALL;
james@532 141 }
james@532 142 }
james@246 143 pi->ip_version = (pi->header[XN_HDR_SIZE + 0] & 0xF0) >> 4;
james@246 144 if (pi->ip_version != 4)
james@246 145 {
james@246 146 KdPrint((__DRIVER_NAME " ip_version = %d\n", pi->ip_version));
james@246 147 return PARSE_UNKNOWN_TYPE;
james@246 148 }
james@246 149 pi->ip4_header_length = (pi->header[XN_HDR_SIZE + 0] & 0x0F) << 2;
james@554 150 if (pi->header_length < (ULONG)(XN_HDR_SIZE + pi->ip4_header_length + 20))
james@246 151 {
james@532 152 if (!XenNet_BuildHeader(pi, (ULONG)(XN_HDR_SIZE + pi->ip4_header_length + 20)))
james@532 153 {
james@538 154 //KdPrint((__DRIVER_NAME " packet too small (IP Header + IP Options + TCP Header)\n"));
james@532 155 return PARSE_TOO_SMALL;
james@532 156 }
james@246 157 }
james@246 158 break;
james@246 159 default:
james@535 160 //KdPrint((__DRIVER_NAME " Not IP (%d)\n", GET_NET_PUSHORT(&pi->header[12])));
james@246 161 return PARSE_UNKNOWN_TYPE;
james@246 162 }
james@246 163 pi->ip_proto = pi->header[XN_HDR_SIZE + 9];
james@246 164 switch (pi->ip_proto)
james@246 165 {
james@246 166 case 6: // TCP
james@246 167 case 17: // UDP
james@246 168 break;
james@246 169 default:
james@536 170 //KdPrint((__DRIVER_NAME " Not TCP/UDP (%d)\n", pi->ip_proto));
james@246 171 return PARSE_UNKNOWN_TYPE;
james@246 172 }
james@422 173 pi->ip4_length = GET_NET_PUSHORT(&pi->header[XN_HDR_SIZE + 2]);
james@246 174 pi->tcp_header_length = (pi->header[XN_HDR_SIZE + pi->ip4_header_length + 12] & 0xf0) >> 2;
james@253 175
james@532 176 if (pi->header_length < (ULONG)(XN_HDR_SIZE + pi->ip4_header_length + pi->tcp_header_length))
james@253 177 {
james@536 178 if (!XenNet_BuildHeader(pi, (ULONG)(XN_HDR_SIZE + pi->ip4_header_length + pi->tcp_header_length)))
james@532 179 {
james@536 180 //KdPrint((__DRIVER_NAME " packet too small (IP Header + IP Options + TCP Header + TCP Options)\n"));
james@532 181 return PARSE_TOO_SMALL;
james@532 182 }
james@253 183 }
james@615 184
james@615 185 if ((ULONG)XN_HDR_SIZE + pi->ip4_length > pi->total_length)
james@615 186 {
james@615 187 KdPrint((__DRIVER_NAME " XN_HDR_SIZE + ip4_length (%d) > total_length (%d)\n", XN_HDR_SIZE + pi->ip4_length, pi->total_length));
james@615 188 return PARSE_UNKNOWN_TYPE;
james@615 189 }
james@253 190
james@246 191 pi->tcp_length = pi->ip4_length - pi->ip4_header_length - pi->tcp_header_length;
james@246 192 pi->tcp_remaining = pi->tcp_length;
james@422 193 pi->tcp_seq = GET_NET_PULONG(&pi->header[XN_HDR_SIZE + pi->ip4_header_length + 4]);
james@459 194 pi->tcp_has_options = (BOOLEAN)(pi->tcp_header_length > 20);
james@246 195 if (pi->mss > 0 && pi->tcp_length > pi->mss)
james@246 196 pi->split_required = TRUE;
james@532 197
james@532 198 //KdPrint((__DRIVER_NAME " ip4_length = %d\n", pi->ip4_length));
james@532 199 //KdPrint((__DRIVER_NAME " tcp_length = %d\n", pi->tcp_length));
james@532 200 //FUNCTION_EXIT();
james@532 201
james@246 202 return PARSE_OK;
james@246 203 }
james@246 204
james@246 205 VOID
james@246 206 XenNet_SumIpHeader(
james@248 207 PUCHAR header,
james@248 208 USHORT ip4_header_length
james@246 209 )
james@246 210 {
james@246 211 ULONG csum = 0;
james@246 212 USHORT i;
james@246 213
james@254 214 ASSERT(ip4_header_length > 12);
james@254 215 ASSERT(!(ip4_header_length & 1));
james@254 216
james@248 217 header[XN_HDR_SIZE + 10] = 0;
james@248 218 header[XN_HDR_SIZE + 11] = 0;
james@248 219 for (i = 0; i < ip4_header_length; i += 2)
james@246 220 {
james@422 221 csum += GET_NET_PUSHORT(&header[XN_HDR_SIZE + i]);
james@246 222 }
james@246 223 while (csum & 0xFFFF0000)
james@246 224 csum = (csum & 0xFFFF) + (csum >> 16);
james@246 225 csum = ~csum;
james@422 226 SET_NET_USHORT(&header[XN_HDR_SIZE + 10], (USHORT)csum);
james@246 227 }