Page MenuHomeFreeBSD

D56637.diff
No OneTemporary

D56637.diff

diff --git a/sbin/dhclient/Makefile b/sbin/dhclient/Makefile
--- a/sbin/dhclient/Makefile
+++ b/sbin/dhclient/Makefile
@@ -36,7 +36,7 @@
PACKAGE=dhclient
SRCS= dhclient.c clparse.c alloc.c dispatch.c hash.c bpf.c options.c \
tree.c conflex.c errwarn.c inet.c packet.c convert.c tables.c \
- parse.c privsep.c
+ parse.c privsep.c inet6.c
PROG= dhclient
SCRIPTS=dhclient-script
@@ -44,6 +44,14 @@
dhclient-script.8
LIBADD= util
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+=-DINET6
+.endif
+
+.if ${MK_NETLINK_SUPPORT} == "no"
+CFLAGS+=-DWITHOUT_NETLINK
+.endif
+
.if ${MK_DYNAMICROOT} == "no"
.warning ${PROG} built without libcasper support
.elif ${MK_CASPER} != "no" && !defined(RESCUE)
diff --git a/sbin/dhclient/clparse.c b/sbin/dhclient/clparse.c
--- a/sbin/dhclient/clparse.c
+++ b/sbin/dhclient/clparse.c
@@ -51,6 +51,27 @@
static char client_script_name[] = "/sbin/dhclient-script";
+/*
+ * Options the user ignores must not be asked for either.
+ * This matters for the IPv6-Only Preferred option.
+ */
+static void
+unrequest_ignored_options(struct client_config *config)
+{
+ bool ignored[256] = {};
+ int i, n;
+
+ for (i = 0; config->ignored_options[i] != 0; i++)
+ ignored[config->ignored_options[i]] = true;
+
+ for (i = n = 0; i < config->requested_option_count; i++) {
+ if (ignored[config->requested_options[i]])
+ continue;
+ config->requested_options[n++] = config->requested_options[i];
+ }
+ config->requested_option_count = n;
+}
+
/*
* client-conf-file :== client-declarations EOF
* client-declarations :== <nil>
@@ -104,6 +125,15 @@
[top_level_config.requested_option_count++] = DHO_DOMAIN_SEARCH;
top_level_config.requested_options
[top_level_config.requested_option_count++] = DHO_INTERFACE_MTU;
+#ifdef INET6
+ /*
+ * RFC 8925 sec 3.2: The DHCPv4 client on an IPv4-requiring host MUST
+ * NOT include the IPv6-Only Preferred option code in the Parameter
+ * Request List.
+ */
+ top_level_config.requested_options
+ [top_level_config.requested_option_count++] = DHO_IPV6_ONLY;
+#endif
if ((cfile = fopen(path_dhclient_conf, "r")) != NULL) {
do {
@@ -137,6 +167,7 @@
}
ifi->client->config = config;
}
+ unrequest_ignored_options(ifi->client->config);
return (!warnings_occurred);
}
diff --git a/sbin/dhclient/dhclient.c b/sbin/dhclient/dhclient.c
--- a/sbin/dhclient/dhclient.c
+++ b/sbin/dhclient/dhclient.c
@@ -128,6 +128,10 @@
static int unknown_ok = 1;
static int routefd;
+#ifndef WITHOUT_NETLINK
+struct snl_state nl_ss;
+#endif
+
struct interface_info *ifi;
int findproto(char *, int);
@@ -141,6 +145,10 @@
int check_search(const char *srch);
const char *option_as_string(unsigned int code, unsigned char *data, int len);
int fork_privchld(int, int);
+static bool ipv6_only_preferred(struct interface_info *, struct packet *);
+static void v6only_wait_expired(void *);
+static void make_release(struct interface_info *, struct client_lease *);
+static void send_release(struct interface_info *, struct client_lease *);
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
@@ -534,6 +542,14 @@
if (caph_rights_limit(routefd, &rights) < 0)
error("can't limit route socket: %m");
+#ifndef WITHOUT_NETLINK
+ if (!snl_init(&nl_ss, NETLINK_ROUTE))
+ error("can't open netlink socket");
+ cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE);
+ if (caph_rights_limit(nl_ss.fd, &rights) < 0)
+ error("can't limit netlink route socket: %m");
+#endif
+
endpwent();
setproctitle("%s", ifi->name);
@@ -619,6 +635,8 @@
{
struct interface_info *ip = ipp;
+ cancel_timeout(v6only_wait_expired, ip);
+
/* If we don't remember an active lease, go straight to INIT. */
if (!ip->client->active || ip->client->active->is_bootp) {
state_init(ip);
@@ -658,6 +676,8 @@
ASSERT_STATE(state, S_INIT);
+ cancel_timeout(v6only_wait_expired, ip);
+
/* Make a DHCPDISCOVER packet, and set appropriate per-interface
flags. */
make_discover(ip, ip->client->active);
@@ -762,8 +782,74 @@
send_request(ip);
}
-/* state_requesting is called when we receive a DHCPACK message after
- having sent out one or more DHCPREQUEST packets. */
+/*
+ * state_requesting is called when we receive a DHCPACK message after
+ * RFC 8925, sec 3.2: if the packet carries a valid IPv6-Only Preferred
+ * option and we have IPv6 connectivity, stop DHCPv4 for V6ONLY_WAIT
+ * seconds or until a network attachment event, whichever comes first.
+ * Returns true if DHCPv4 was stopped.
+ */
+static bool
+ipv6_only_preferred(struct interface_info *ip, struct packet *packet)
+{
+ struct client_lease *lp, *next;
+ uint32_t v6wait;
+
+ if (packet->options[DHO_IPV6_ONLY].data == NULL)
+ return (false);
+
+ if (!check_ipv6_connectivity(ip->index)) {
+ note("IPv6-Only Preferred option received, "
+ "but we can't verify IPv6 connectivity, ignore");
+ return (false);
+ }
+
+ v6wait = getULong(packet->options[DHO_IPV6_ONLY].data);
+ note("IPv6-Only Preferred option received (%u seconds), abort", v6wait);
+
+ cancel_timeout(send_discover, ip);
+ cancel_timeout(send_request, ip);
+ cancel_timeout(state_selecting, ip);
+ for (lp = ip->client->offered_leases; lp != NULL; lp = next) {
+ next = lp->next;
+ free_client_lease(lp);
+ }
+ ip->client->offered_leases = NULL;
+
+ /*
+ * In INIT-REBOOT the DHCPACK just re-confirmed our old lease.
+ * Release it so the server doesn't keep the address committed,
+ * and forget it so the next attempt starts with a DHCPDISCOVER.
+ */
+ if (ip->client->state == S_REBOOTING && ip->client->active != NULL) {
+ make_release(ip, ip->client->active);
+ send_release(ip, ip->client->active);
+ disassoc(ip);
+ free_client_lease(ip->client->active);
+ ip->client->active = NULL;
+ rewrite_client_leases();
+ }
+
+ ip->client->state = S_INIT;
+ if (v6wait < UINT32_MAX) {
+ struct timespec stop_time, v6wait_left = {
+ .tv_sec = (time_t)v6wait
+ };
+ timespecadd(&time_now, &v6wait_left, &stop_time);
+ add_timeout_timespec(stop_time, v6only_wait_expired, ip);
+ }
+ go_daemon();
+ return (true);
+}
+
+static void
+v6only_wait_expired(void *ipp)
+{
+ struct interface_info *ip = ipp;
+
+ note("V6ONLY_WAIT expired, restarting DHCPv4");
+ state_reboot(ip);
+}
void
dhcpack(struct packet *packet)
@@ -787,6 +873,11 @@
note("DHCPACK from %s", piaddr(packet->client_addr));
+ /* RFC 8925, sec 3.2: only INIT-REBOOT stops, other states keep the lease. */
+ if (ip->client->state == S_REBOOTING &&
+ ipv6_only_preferred(ip, packet))
+ return;
+
lease = packet_to_lease(packet);
if (!lease) {
note("packet_to_lease failed.");
@@ -1069,6 +1160,10 @@
}
}
+ /* RFC 8925, sec 3.2: with v6only, don't request the offered address. */
+ if (ipv6_only_preferred(ip, packet))
+ return;
+
lease = packet_to_lease(packet);
if (!lease) {
note("packet_to_lease failed.");
@@ -1663,6 +1758,23 @@
ip->client->packet_length, inaddr_any, inaddr_broadcast);
}
+static void
+send_release(struct interface_info *ip, struct client_lease *lease)
+{
+ struct in_addr from, to;
+
+ /* RFC 2131, sec 4.4.4: DHCPRELEASE is unicast to the server. */
+ memcpy(&from, lease->address.iabuf, sizeof(from));
+ if (lease->options[DHO_DHCP_SERVER_IDENTIFIER].len == sizeof(to))
+ memcpy(&to, lease->options[DHO_DHCP_SERVER_IDENTIFIER].data, sizeof(to));
+ else
+ to = inaddr_broadcast;
+
+ note("DHCPRELEASE on %s to %s port %d", ip->name, inet_ntoa(to), REMOTE_PORT);
+ send_packet_unpriv(privfd, &ip->client->packet,
+ ip->client->packet_length, from, to);
+}
+
void
make_discover(struct interface_info *ip, struct client_lease *lease)
{
@@ -1909,24 +2021,26 @@
ip->hw_address.haddr, ip->hw_address.hlen);
}
-void
-make_decline(struct interface_info *ip, struct client_lease *lease)
+/*
+ * Build the packet DHCPDECLINE and DHCPRELEASE share: message type,
+ * server and client identifiers, plus any options already in options[].
+ */
+static void
+make_decline_or_release(struct interface_info *ip,
+ struct client_lease *lease, unsigned char type,
+ struct tree_cache **options)
{
- struct tree_cache *options[256], message_type_tree;
- struct tree_cache requested_address_tree;
- struct tree_cache server_id_tree, client_id_tree;
- unsigned char decline = DHCPDECLINE;
+ struct tree_cache message_type_tree, server_id_tree, client_id_tree;
int i;
- memset(options, 0, sizeof(options));
memset(&ip->client->packet, 0, sizeof(ip->client->packet));
- /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */
+ /* Set DHCP_MESSAGE_TYPE */
i = DHO_DHCP_MESSAGE_TYPE;
options[i] = &message_type_tree;
- options[i]->value = &decline;
- options[i]->len = sizeof(decline);
- options[i]->buf_size = sizeof(decline);
+ options[i]->value = &type;
+ options[i]->len = sizeof(type);
+ options[i]->buf_size = sizeof(type);
options[i]->timeout = 0xFFFFFFFF;
/* Send back the server identifier... */
@@ -1937,14 +2051,6 @@
options[i]->buf_size = lease->options[i].len;
options[i]->timeout = 0xFFFFFFFF;
- /* Send back the address we're declining. */
- i = DHO_DHCP_REQUESTED_ADDRESS;
- options[i] = &requested_address_tree;
- options[i]->value = lease->address.iabuf;
- options[i]->len = lease->address.len;
- options[i]->buf_size = lease->address.len;
- options[i]->timeout = 0xFFFFFFFF;
-
/* Send the uid if the user supplied one. */
i = DHO_DHCP_CLIENT_IDENTIFIER;
if (ip->client->config->send_options[i].len) {
@@ -1955,7 +2061,6 @@
options[i]->timeout = 0xFFFFFFFF;
}
-
/* Set up the option buffer... */
ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
options, 0, 0, 0, NULL, 0);
@@ -1983,6 +2088,38 @@
ip->hw_address.haddr, ip->hw_address.hlen);
}
+void
+make_decline(struct interface_info *ip, struct client_lease *lease)
+{
+ struct tree_cache *options[256], requested_address_tree;
+ int i;
+
+ memset(options, 0, sizeof(options));
+
+ /* Send back the address we're declining. */
+ i = DHO_DHCP_REQUESTED_ADDRESS;
+ options[i] = &requested_address_tree;
+ options[i]->value = lease->address.iabuf;
+ options[i]->len = lease->address.len;
+ options[i]->buf_size = lease->address.len;
+ options[i]->timeout = 0xFFFFFFFF;
+
+ make_decline_or_release(ip, lease, DHCPDECLINE, options);
+}
+
+static void
+make_release(struct interface_info *ip, struct client_lease *lease)
+{
+ struct tree_cache *options[256];
+
+ memset(options, 0, sizeof(options));
+ make_decline_or_release(ip, lease, DHCPRELEASE, options);
+
+ /* RFC 2131, sec 4.4.4: ciaddr carries the address being released. */
+ memcpy(&ip->client->packet.ciaddr, lease->address.iabuf,
+ sizeof(ip->client->packet.ciaddr));
+}
+
void
free_client_lease(struct client_lease *lease)
{
@@ -2682,6 +2819,7 @@
case DHO_SIP_SERVERS:
case DHO_V_I_VENDOR_CLASS:
case DHO_V_I_VENDOR_OPTS:
+ case DHO_IPV6_ONLY:
case DHO_END:
return (1);
case DHO_CLASSLESS_ROUTES:
diff --git a/sbin/dhclient/dhcp-options.5 b/sbin/dhclient/dhcp-options.5
--- a/sbin/dhclient/dhcp-options.5
+++ b/sbin/dhclient/dhcp-options.5
@@ -372,6 +372,12 @@
To specify the default route, use the
.Ic routers
option.
+.It Ic option ipv6only Ar uint32 ;
+This option specifies the number of seconds for which the client should disable
+DHCPv4 configuration provided by the DHCP server.
+The IPv6-Only Preferred option will check if the host is IPv6-Only capable or
+not.
+The time is specified as a 32-bit unsigned integer.
.El
.Ss Link Layer Parameters per Interface
.Bl -tag -width indent
@@ -596,7 +602,7 @@
.Xr dhclient 8 ,
.Xr dhcpd 8
.Rs
-.%R "RFC 2131, RFC 2132, RFC 3769"
+.%R "RFC 2131, RFC 2132, RFC 3769, RFC 8925"
.Re
.Sh AUTHORS
.An -nosplit
diff --git a/sbin/dhclient/dhcp.h b/sbin/dhclient/dhcp.h
--- a/sbin/dhclient/dhcp.h
+++ b/sbin/dhclient/dhcp.h
@@ -92,6 +92,8 @@
extensions field). */
#define DHCP_OPTIONS_COOKIE "\143\202\123\143"
+#define MIN_V6ONLY_WAIT 300 /* RFC 8925 */
+
/* DHCP Option codes: */
#define DHO_PAD 0
@@ -170,6 +172,7 @@
#define DHO_STREETTALK_SERVER 75
#define DHO_STREETTALK_DA_SERVER 76
#define DHO_DHCP_USER_CLASS_ID 77
+#define DHO_IPV6_ONLY 108
#define DHO_URL 114
#define DHO_DOMAIN_SEARCH 119
#define DHO_SIP_SERVERS 120
diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h
--- a/sbin/dhclient/dhcpd.h
+++ b/sbin/dhclient/dhcpd.h
@@ -54,6 +54,10 @@
#include <net/if_dl.h>
#include <net/route.h>
+#ifndef WITHOUT_NETLINK
+#include <netlink/netlink_snl.h>
+#endif
+
#include <netinet/in.h>
#include <arpa/inet.h>
@@ -357,6 +361,9 @@
int addr_eq(struct iaddr, struct iaddr);
char *piaddr(struct iaddr);
+/* inet6.c */
+bool check_ipv6_connectivity(uint16_t ifindex);
+
/* dhclient.c */
extern cap_channel_t *capsyslog;
extern const char *path_dhclient_conf;
@@ -372,6 +379,10 @@
extern struct interface_info *ifi;
+#ifndef WITHOUT_NETLINK
+extern struct snl_state nl_ss;
+#endif
+
void dhcpoffer(struct packet *);
void dhcpack(struct packet *);
void dhcpnak(struct packet *);
diff --git a/sbin/dhclient/inet6.c b/sbin/dhclient/inet6.c
new file mode 100644
--- /dev/null
+++ b/sbin/dhclient/inet6.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2026 Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#ifndef WITHOUT_NETLINK
+#include <netlink/netlink.h>
+#include <netlink/netlink_route.h>
+#include <netlink/netlink_snl.h>
+#include <netlink/netlink_snl_route.h>
+#include <netlink/netlink_snl_route_compat.h>
+#include <netlink/netlink_snl_route_parsers.h>
+#endif
+
+#include "dhcpd.h"
+
+#ifndef WITHOUT_NETLINK
+/*
+ * Check if the interface has a routable IPv6 address, if true
+ * assume it has IPv6 connectivity.
+ * Returns true if a unicast IPv6 address with non-link-local scope
+ * is found, false otherwise.
+ */
+static bool
+check_ipv6_address(struct snl_state *ss, uint16_t ifindex)
+{
+ struct snl_writer nw;
+ struct snl_errmsg_data e = {};
+ struct snl_parsed_addr addr = {};
+ struct nlmsghdr *hdr, *rx_hdr;
+ struct ifaddrmsg *ifahdr;
+
+ snl_init_writer(ss, &nw);
+ hdr = snl_create_msg_request(&nw, RTM_GETADDR);
+ hdr->nlmsg_flags |= NLM_F_DUMP;
+ ifahdr = snl_reserve_msg_object(&nw, struct ifaddrmsg);
+ ifahdr->ifa_family = AF_INET6;
+ ifahdr->ifa_index = ifindex;
+
+ if ((hdr = snl_finalize_msg(&nw)) == NULL || !snl_send_message(ss, hdr))
+ return (false);
+
+ while ((rx_hdr = snl_read_reply_multi(ss, hdr->nlmsg_seq, &e)) != NULL) {
+ struct sockaddr_in6 *sin6;
+
+ if (!snl_parse_nlmsg(ss, rx_hdr, &snl_rtm_addr_parser, &addr))
+ continue;
+
+ if (addr.ifa_address != NULL) {
+ sin6 = (struct sockaddr_in6 *)addr.ifa_address;
+ if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ return (true);
+ }
+ }
+ return (false);
+}
+
+/*
+ * If a default route is found, assume IPv6 connectivity.
+ * Returns true if found, false otherwise.
+ */
+static bool
+check_ipv6_defaultroute(struct snl_state *ss)
+{
+ struct snl_writer nw;
+ struct snl_parsed_route r = {};
+ struct in6_addr in6 = IN6ADDR_ANY_INIT;
+ struct nlmsghdr *hdr, *rx_hdr;
+ struct rtmsg *rtmsg;
+
+ snl_init_writer(ss, &nw);
+ hdr = snl_create_msg_request(&nw, RTM_GETROUTE);
+ rtmsg = snl_reserve_msg_object(&nw, struct rtmsg);
+ rtmsg->rtm_family = AF_INET6;
+ rtmsg->rtm_dst_len = 0;
+ rtmsg->rtm_type = RTN_UNICAST;
+ rtmsg->rtm_flags = RTM_F_PREFIX;
+ snl_add_msg_attr_ip6(&nw, RTA_DST, &in6);
+
+ if ((hdr = snl_finalize_msg(&nw)) == NULL || !snl_send_message(ss, hdr))
+ return (false);
+
+ rx_hdr = snl_read_reply(ss, hdr->nlmsg_seq);
+ if (rx_hdr == NULL || rx_hdr->nlmsg_type != NL_RTM_NEWROUTE)
+ return (false);
+
+ if (!snl_parse_nlmsg(ss, rx_hdr, &snl_rtm_route_parser, &r))
+ return (false);
+
+ if (r.rta_gw == NULL)
+ return (false);
+
+ return (true);
+}
+#endif
+
+bool
+check_ipv6_connectivity(uint16_t ifindex __unused)
+{
+ bool ret = false;
+
+#ifndef WITHOUT_NETLINK
+ /* Return true if the interface has a routable ipv6 address */
+ ret = check_ipv6_address(&nl_ss, ifindex);
+
+ /* A default route using a link-local address may exist. */
+ if (!ret)
+ ret = check_ipv6_defaultroute(&nl_ss);
+ snl_clear_lb(&nl_ss);
+#endif
+
+ return (ret);
+}
diff --git a/sbin/dhclient/options.c b/sbin/dhclient/options.c
--- a/sbin/dhclient/options.c
+++ b/sbin/dhclient/options.c
@@ -59,6 +59,7 @@
int find_search_domain_name_len(struct option_data *option, size_t *offset);
void expand_search_domain_name(struct option_data *option, size_t *offset,
unsigned char **domain_search);
+void ipv6_only_option(struct packet *packet);
/*
@@ -99,10 +100,12 @@
sizeof(packet->raw->sname));
}
- /* Expand DHCP Domain Search option. */
+ /* Process options */
if (packet->options_valid) {
expand_domain_search(packet);
+ ipv6_only_option(packet);
}
+
}
/*
@@ -372,6 +375,69 @@
}
}
+/*
+ * process ipv6_only option.
+ * See: RFC 8925
+ */
+void
+ipv6_only_option(struct packet *packet)
+{
+ struct option_data *option;
+ struct client_config *config;
+ uint32_t val;
+ bool requested;
+
+ if (packet->options[DHO_IPV6_ONLY].data == NULL)
+ return;
+
+ option = &packet->options[DHO_IPV6_ONLY];
+ config = packet->interface->client->config;
+
+ /*
+ * RFC 8925, sec 3.1: The client MUST ignore the IPv6-Only Preferred
+ * option if the length field value is not 4.
+ */
+ if (option->len != 4) {
+ warning("IPv6-Only preferred option length is invalid");
+ goto bad;
+ }
+ val = getULong(option->data);
+
+ /*
+ * RFC 8925, sec 3.2: If the client did not include the IPv6-Only Preferred
+ * option code in the Parameter Request List in the DHCPDISCOVER or
+ * DHCPREQUEST message, it MUST ignore the IPv6-Only Preferred option
+ * in any messages received from the server.
+ */
+ requested = false;
+ for (int i = 0; i < config->requested_option_count; i++) {
+ if (config->requested_options[i] == DHO_IPV6_ONLY) {
+ requested = true;
+ break;
+ }
+ }
+ if (!requested) {
+ note("Unwanted IPv6-Only Preferred option received, ignore it");
+ goto bad;
+ }
+
+ /*
+ * If the IPv6-Only Preferred option returned by the server contains
+ * a value greater than or equal to MIN_V6ONLY_WAIT, the client SHOULD
+ * set the V6ONLY_WAIT timer to that value.
+ * Otherwise, the client SHOULD set the V6ONLY_WAIT timer
+ * to MIN_V6ONLY_WAIT.
+ */
+ if (val < MIN_V6ONLY_WAIT)
+ putULong(option->data, MIN_V6ONLY_WAIT);
+
+ return;
+bad:
+ free(option->data);
+ option->len = 0;
+ option->data = NULL;
+}
+
/*
* cons options into a big buffer, and then split them out into the
* three separate buffers if needed. This allows us to cons up a set of
diff --git a/sbin/dhclient/tables.c b/sbin/dhclient/tables.c
--- a/sbin/dhclient/tables.c
+++ b/sbin/dhclient/tables.c
@@ -173,7 +173,7 @@
{ "option-105", "X", &dhcp_universe, 105 },
{ "option-106", "X", &dhcp_universe, 106 },
{ "option-107", "X", &dhcp_universe, 107 },
- { "option-108", "X", &dhcp_universe, 108 },
+ { "ipv6only", "L", &dhcp_universe, 108 },
{ "option-109", "X", &dhcp_universe, 109 },
{ "option-110", "X", &dhcp_universe, 110 },
{ "option-111", "X", &dhcp_universe, 111 },
@@ -403,11 +403,12 @@
DHO_DHCP_USER_CLASS_ID,
DHO_DOMAIN_SEARCH,
DHO_URL,
+ DHO_IPV6_ONLY,
/* Presently-undefined options... */
62, 63, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
- 106, 107, 108, 109, 110, 111, 112, 113, 115, 116, 117,
+ 106, 107, 109, 110, 111, 112, 113, 115, 116, 117,
118, 120, 122, 123, 124, 125, 126, 127, 128, 129, 130,
131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154,

File Metadata

Mime Type
text/plain
Expires
Fri, Oct 2, 6:31 AM (11 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
40065600
Default Alt Text
D56637.diff (19 KB)

Event Timeline