ng_device.c revision 139823
1193323Sed/*- 2193323Sed * Copyright (c) 2002 Mark Santcroos <marks@ripe.net> 3193323Sed * Copyright (c) 2004 Gleb Smirnoff <glebius@FreeBSD.org> 4193323Sed * 5193323Sed * Redistribution and use in source and binary forms, with or without 6193323Sed * modification, are permitted provided that the following conditions 7193323Sed * are met: 8193323Sed * 1. Redistributions of source code must retain the above copyright 9193323Sed * notice, this list of conditions and the following disclaimer. 10193323Sed * 2. Redistributions in binary form must reproduce the above copyright 11193323Sed * notice, this list of conditions and the following disclaimer in the 12193323Sed * documentation and/or other materials provided with the distribution. 13193323Sed * 14193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15193323Sed * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16193323Sed * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17193323Sed * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18193323Sed * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19193323Sed * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20198090Srdivacky * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21198892Srdivacky * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22193323Sed * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23193323Sed * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24193323Sed * 25193323Sed * Netgraph "device" node 26193323Sed * 27193323Sed * This node presents a /dev/ngd%d device that interfaces to an other 28193323Sed * netgraph node. 29193323Sed * 30193323Sed * $FreeBSD: head/sys/netgraph/ng_device.c 139823 2005-01-07 01:45:51Z imp $ 31193323Sed * 32198090Srdivacky */ 33198090Srdivacky 34193323Sed#if 0 35199481Srdivacky#define DBG do { printf("ng_device: %s\n", __func__ ); } while (0) 36193323Sed#else 37193323Sed#define DBG do {} while (0) 38193323Sed#endif 39193323Sed 40193323Sed#include <sys/param.h> 41193323Sed#include <sys/conf.h> 42193323Sed#include <sys/ioccom.h> 43193323Sed#include <sys/kernel.h> 44199511Srdivacky#include <sys/malloc.h> 45199481Srdivacky#include <sys/mbuf.h> 46193323Sed#include <sys/poll.h> 47199511Srdivacky#include <sys/queue.h> 48193323Sed#include <sys/socket.h> 49193323Sed#include <sys/systm.h> 50199481Srdivacky#include <sys/uio.h> 51193323Sed#include <sys/vnode.h> 52193323Sed 53193323Sed#include <net/if.h> 54199481Srdivacky#include <net/if_var.h> 55199481Srdivacky#include <netinet/in.h> 56199481Srdivacky#include <netinet/in_systm.h> 57199481Srdivacky#include <netinet/ip.h> 58199481Srdivacky 59199481Srdivacky#include <netgraph/ng_message.h> 60193323Sed#include <netgraph/netgraph.h> 61199481Srdivacky#include <netgraph/ng_device.h> 62199481Srdivacky 63199481Srdivacky#define ERROUT(x) do { error = (x); goto done; } while (0) 64199481Srdivacky 65199481Srdivacky/* Netgraph methods */ 66199481Srdivackystatic ng_constructor_t ng_device_constructor; 67199481Srdivackystatic ng_rcvmsg_t ng_device_rcvmsg; 68199481Srdivackystatic ng_shutdown_t ng_device_shutdown; 69199481Srdivackystatic ng_newhook_t ng_device_newhook; 70199481Srdivackystatic ng_rcvdata_t ng_device_rcvdata; 71199481Srdivackystatic ng_disconnect_t ng_device_disconnect; 72199481Srdivacky 73199481Srdivacky/* Netgraph type */ 74199481Srdivackystatic struct ng_type ngd_typestruct = { 75198090Srdivacky .version = NG_ABI_VERSION, 76193323Sed .name = NG_DEVICE_NODE_TYPE, 77199481Srdivacky .constructor = ng_device_constructor, 78198090Srdivacky .rcvmsg = ng_device_rcvmsg, 79198090Srdivacky .shutdown = ng_device_shutdown, 80193323Sed .newhook = ng_device_newhook, 81198090Srdivacky .rcvdata = ng_device_rcvdata, 82198090Srdivacky .disconnect = ng_device_disconnect, 83198090Srdivacky}; 84198090SrdivackyNETGRAPH_INIT(device, &ngd_typestruct); 85198090Srdivacky 86198090Srdivacky/* per node data */ 87193323Sedstruct ngd_private { 88198090Srdivacky struct ifqueue readq; 89198090Srdivacky SLIST_ENTRY(ngd_private) links; 90198090Srdivacky struct ng_node *node; 91198090Srdivacky struct ng_hook *hook; 92198090Srdivacky struct cdev *ngddev; 93198090Srdivacky struct mtx ngd_mtx; 94198090Srdivacky int unit; 95193323Sed uint16_t flags; 96193323Sed#define NGDF_OPEN 0x0001 97193323Sed#define NGDF_RWAIT 0x0002 98193323Sed}; 99193323Sedtypedef struct ngd_private *priv_p; 100193323Sed 101201360Srdivacky/* List of all active nodes and mutex to protect it */ 102199481Srdivackystatic SLIST_HEAD(, ngd_private) ngd_nodes = SLIST_HEAD_INITIALIZER(ngd_nodes); 103193323Sedstatic struct mtx ng_device_mtx; 104193323SedMTX_SYSINIT(ng_device, &ng_device_mtx, "ng_device", MTX_DEF); 105193323Sed 106193323Sed/* Maximum number of NGD devices */ 107199481Srdivacky#define MAX_NGD 25 /* should be more than enough for now */ 108193323Sed 109193323Sedstatic d_close_t ngdclose; 110193323Sedstatic d_open_t ngdopen; 111193323Sedstatic d_read_t ngdread; 112193323Sedstatic d_write_t ngdwrite; 113193323Sed#if 0 114193323Sedstatic d_ioctl_t ngdioctl; 115193323Sed#endif 116193323Sedstatic d_poll_t ngdpoll; 117193323Sed 118199481Srdivackystatic struct cdevsw ngd_cdevsw = { 119193323Sed .d_version = D_VERSION, 120193323Sed .d_open = ngdopen, 121193323Sed .d_close = ngdclose, 122193323Sed .d_read = ngdread, 123193323Sed .d_write = ngdwrite, 124193323Sed#if 0 125193323Sed .d_ioctl = ngdioctl, 126193323Sed#endif 127193323Sed .d_poll = ngdpoll, 128193323Sed .d_name = NG_DEVICE_DEVNAME, 129193323Sed}; 130193323Sed 131193323Sed/* Helper functions */ 132193323Sedstatic int get_free_unit(void); 133193323Sed 134193323Sed/****************************************************************************** 135193323Sed * Netgraph methods 136203954Srdivacky ******************************************************************************/ 137193323Sed 138193323Sed/* 139193323Sed * create new node 140198090Srdivacky */ 141193323Sedstatic int 142193323Sedng_device_constructor(node_p node) 143193323Sed{ 144193323Sed priv_p priv; 145193323Sed 146193323Sed DBG; 147193323Sed 148193323Sed MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); 149193323Sed if (priv == NULL) 150193323Sed return (ENOMEM); 151193323Sed 152193323Sed mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF); 153193323Sed mtx_lock(&priv->ngd_mtx); 154193323Sed 155193323Sed mtx_lock(&ng_device_mtx); 156193323Sed 157193323Sed priv->unit = get_free_unit(); 158193323Sed if(priv->unit < 0) { 159193323Sed printf("%s: No free unit found by get_free_unit(), " 160193323Sed "increase MAX_NGD\n",__func__); 161193323Sed mtx_unlock(&ng_device_mtx); 162193323Sed mtx_destroy(&priv->ngd_mtx); 163193323Sed FREE(priv, M_NETGRAPH); 164193323Sed return(EINVAL); 165193323Sed } 166193323Sed 167193323Sed priv->ngddev = make_dev(&ngd_cdevsw, unit2minor(priv->unit), UID_ROOT, 168193323Sed GID_WHEEL, 0600, NG_DEVICE_DEVNAME "%d", priv->unit); 169193323Sed if(priv->ngddev == NULL) { 170193323Sed printf("%s(): make_dev() failed\n",__func__); 171193323Sed mtx_unlock(&ng_device_mtx); 172193323Sed mtx_destroy(&priv->ngd_mtx); 173193323Sed FREE(priv, M_NETGRAPH); 174198090Srdivacky return(EINVAL); 175198090Srdivacky } 176198090Srdivacky 177198090Srdivacky SLIST_INSERT_HEAD(&ngd_nodes, priv, links); 178198090Srdivacky 179198090Srdivacky mtx_unlock(&ng_device_mtx); 180198090Srdivacky 181193323Sed mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF); 182198090Srdivacky IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen); 183198090Srdivacky 184198090Srdivacky /* Link everything together */ 185193323Sed NG_NODE_SET_PRIVATE(node, priv); 186198090Srdivacky priv->node = node; 187198090Srdivacky priv->ngddev->si_drv1 = priv; 188193323Sed 189198090Srdivacky mtx_unlock(&priv->ngd_mtx); 190193323Sed 191193323Sed return(0); 192193323Sed} 193193323Sed 194198090Srdivacky/* 195198090Srdivacky * Process control message. 196193323Sed */ 197193323Sed 198193323Sedstatic int 199193323Sedng_device_rcvmsg(node_p node, item_p item, hook_p lasthook) 200193323Sed{ 201193323Sed const priv_p priv = NG_NODE_PRIVATE(node); 202193323Sed struct ng_mesg *msg; 203198090Srdivacky struct ng_mesg *resp = NULL; 204193323Sed int error = 0; 205193323Sed 206193323Sed NGI_GET_MSG(item, msg); 207193323Sed 208193323Sed if (msg->header.typecookie == NGM_DEVICE_COOKIE) { 209203954Srdivacky switch (msg->header.cmd) { 210203954Srdivacky case NGM_DEVICE_GET_DEVNAME: 211203954Srdivacky /* XXX: Fix when NGD_MAX us bigger */ 212203954Srdivacky NG_MKRESPONSE(resp, msg, 213203954Srdivacky strlen(NG_DEVICE_DEVNAME) + 3, M_NOWAIT); 214193323Sed 215203954Srdivacky if (resp == NULL) 216203954Srdivacky ERROUT(ENOMEM); 217203954Srdivacky 218199481Srdivacky strlcpy((char *)resp->data, priv->ngddev->si_name, 219203954Srdivacky strlen(priv->ngddev->si_name) + 1); 220203954Srdivacky break; 221193323Sed 222203954Srdivacky default: 223203954Srdivacky error = EINVAL; 224203954Srdivacky break; 225203954Srdivacky } 226203954Srdivacky } else 227203954Srdivacky error = EINVAL; 228203954Srdivacky 229203954Srdivackydone: 230199481Srdivacky NG_RESPOND_MSG(error, node, item, resp); 231203954Srdivacky NG_FREE_MSG(msg); 232203954Srdivacky return (error); 233203954Srdivacky} 234203954Srdivacky 235203954Srdivacky/* 236203954Srdivacky * Accept incoming hook. We support only one hook per node. 237203954Srdivacky */ 238203954Srdivackystatic int 239203954Srdivackyng_device_newhook(node_p node, hook_p hook, const char *name) 240203954Srdivacky{ 241203954Srdivacky priv_p priv = NG_NODE_PRIVATE(node); 242203954Srdivacky 243203954Srdivacky DBG; 244193323Sed 245203954Srdivacky /* We have only one hook per node */ 246203954Srdivacky if (priv->hook != NULL) 247193323Sed return (EISCONN); 248193323Sed 249198090Srdivacky priv->hook = hook; 250203954Srdivacky 251203954Srdivacky return(0); 252203954Srdivacky} 253203954Srdivacky 254203954Srdivacky/* 255203954Srdivacky * Receive data from hook, write it to device. 256203954Srdivacky */ 257203954Srdivackystatic int 258203954Srdivackyng_device_rcvdata(hook_p hook, item_p item) 259193323Sed{ 260198090Srdivacky priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 261193323Sed struct mbuf *m; 262193323Sed 263193323Sed DBG; 264193323Sed 265193323Sed NGI_GET_M(item, m); 266193323Sed NG_FREE_ITEM(item); 267193323Sed 268193323Sed IF_LOCK(&priv->readq); 269193323Sed if (_IF_QFULL(&priv->readq)) { 270193323Sed _IF_DROP(&priv->readq); 271193323Sed IF_UNLOCK(&priv->readq); 272199481Srdivacky NG_FREE_M(m); 273193323Sed return (ENOBUFS); 274193323Sed } 275193323Sed 276193323Sed _IF_ENQUEUE(&priv->readq, m); 277193323Sed IF_UNLOCK(&priv->readq); 278193323Sed mtx_lock(&priv->ngd_mtx); 279193323Sed if (priv->flags & NGDF_RWAIT) { 280193323Sed priv->flags &= ~NGDF_RWAIT; 281193323Sed wakeup(priv); 282193323Sed } 283193323Sed mtx_unlock(&priv->ngd_mtx); 284193323Sed 285193323Sed return(0); 286193323Sed} 287193323Sed 288193323Sed/* 289193323Sed * Removal of the hook destroys the node. 290193323Sed */ 291193323Sedstatic int 292193323Sedng_device_disconnect(hook_p hook) 293193323Sed{ 294199481Srdivacky priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 295193323Sed 296193323Sed DBG; 297193323Sed 298193323Sed destroy_dev(priv->ngddev); 299193323Sed mtx_destroy(&priv->ngd_mtx); 300193323Sed 301199481Srdivacky mtx_lock(&ng_device_mtx); 302199481Srdivacky SLIST_REMOVE(&ngd_nodes, priv, ngd_private, links); 303193323Sed mtx_unlock(&ng_device_mtx); 304193323Sed 305193323Sed IF_DRAIN(&priv->readq); 306193323Sed mtx_destroy(&(priv)->readq.ifq_mtx); 307193323Sed 308193323Sed FREE(priv, M_NETGRAPH); 309193323Sed 310193323Sed ng_rmnode_self(NG_HOOK_NODE(hook)); 311199481Srdivacky 312193323Sed return(0); 313193323Sed} 314199481Srdivacky 315193323Sed/* 316193323Sed * Node shutdown. Everything is already done in disconnect method. 317199481Srdivacky */ 318193323Sedstatic int 319193323Sedng_device_shutdown(node_p node) 320193323Sed{ 321193323Sed NG_NODE_UNREF(node); 322193323Sed return (0); 323193323Sed} 324193323Sed 325193323Sed/****************************************************************************** 326193323Sed * Device methods 327193323Sed ******************************************************************************/ 328193323Sed 329193323Sed/* 330193323Sed * the device is opened 331193323Sed */ 332193323Sedstatic int 333199481Srdivackyngdopen(struct cdev *dev, int flag, int mode, struct thread *td) 334193323Sed{ 335193323Sed priv_p priv = (priv_p )dev->si_drv1; 336193323Sed 337204792Srdivacky DBG; 338204792Srdivacky 339204792Srdivacky mtx_lock(&priv->ngd_mtx); 340193323Sed priv->flags |= NGDF_OPEN; 341193323Sed mtx_unlock(&priv->ngd_mtx); 342193323Sed 343193323Sed return(0); 344193323Sed} 345203954Srdivacky 346193323Sed/* 347193323Sed * the device is closed 348193323Sed */ 349193323Sedstatic int 350193323Sedngdclose(struct cdev *dev, int flag, int mode, struct thread *td) 351193323Sed{ 352193323Sed priv_p priv = (priv_p )dev->si_drv1; 353193323Sed 354193323Sed DBG; 355193323Sed mtx_lock(&priv->ngd_mtx); 356193323Sed priv->flags &= ~NGDF_OPEN; 357193323Sed mtx_unlock(&priv->ngd_mtx); 358193323Sed 359193323Sed return(0); 360199481Srdivacky} 361193323Sed 362193323Sed#if 0 /* 363193323Sed * The ioctl is transformed into netgraph control message. 364199481Srdivacky * We do not process them, yet. 365193323Sed */ 366193323Sed/* 367193323Sed * process ioctl 368193323Sed * 369193323Sed * they are translated into netgraph messages and passed on 370193323Sed * 371193323Sed */ 372193323Sedstatic int 373193323Sedngdioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 374193323Sed{ 375193323Sed struct ngd_softc *sc = &ngd_softc; 376193323Sed struct ngd_connection * connection = NULL; 377193323Sed struct ngd_connection * tmp; 378193323Sed int error = 0; 379193323Sed struct ng_mesg *msg; 380193323Sed struct ngd_param_s * datap; 381193323Sed 382193323Sed DBG; 383193323Sed 384193323Sed SLIST_FOREACH(tmp,&sc->head,links) { 385193323Sed if(tmp->ngddev == dev) { 386193323Sed connection = tmp; 387193323Sed } 388193323Sed } 389199481Srdivacky if(connection == NULL) { 390193323Sed printf("%s(): connection is still NULL, no dev found\n",__func__); 391193323Sed return(-1); 392199481Srdivacky } 393193323Sed 394193323Sed NG_MKMESSAGE(msg, NGM_DEVICE_COOKIE, cmd, sizeof(struct ngd_param_s), 395193323Sed M_NOWAIT); 396193323Sed if (msg == NULL) { 397193323Sed printf("%s(): msg == NULL\n",__func__); 398193323Sed goto nomsg; 399193323Sed } 400193323Sed 401198090Srdivacky /* pass the ioctl data into the ->data area */ 402193323Sed datap = (struct ngd_param_s *)msg->data; 403199481Srdivacky datap->p = addr; 404193323Sed 405193323Sed NG_SEND_MSG_HOOK(error, sc->node, msg, connection->active_hook, 0); 406193323Sed if(error) 407193323Sed printf("%s(): NG_SEND_MSG_HOOK error: %d\n",__func__,error); 408193323Sed 409193323Sednomsg: 410193323Sed 411193323Sed return(0); 412193323Sed} 413193323Sed#endif /* if 0 */ 414193323Sed 415193323Sed/* 416193323Sed * This function is called when a read(2) is done to our device. 417204792Srdivacky * We process one mbuf from queue. 418204792Srdivacky */ 419193323Sedstatic int 420193323Sedngdread(struct cdev *dev, struct uio *uio, int flag) 421193323Sed{ 422193323Sed priv_p priv = (priv_p )dev->si_drv1; 423193323Sed struct mbuf *m; 424193323Sed int len, error = 0; 425193323Sed 426193323Sed DBG; 427193323Sed 428193323Sed /* get an mbuf */ 429193323Sed do { 430193323Sed IF_DEQUEUE(&priv->readq, m); 431193323Sed if (m == NULL) { 432193323Sed if (flag & IO_NDELAY) 433193323Sed return (EWOULDBLOCK); 434199481Srdivacky mtx_lock(&priv->ngd_mtx); 435193323Sed priv->flags |= NGDF_RWAIT; 436193323Sed if ((error = msleep(priv, &priv->ngd_mtx, 437200581Srdivacky PDROP | PCATCH | (PZERO + 1), 438193323Sed "ngdread", 0)) != 0) 439193323Sed return (error); 440193323Sed } 441193323Sed } while (m == NULL); 442193323Sed 443193323Sed while (m && uio->uio_resid > 0 && error == 0) { 444193323Sed len = MIN(uio->uio_resid, m->m_len); 445193323Sed if (len != 0) 446193323Sed error = uiomove(mtod(m, void *), len, uio); 447193323Sed m = m_free(m); 448193323Sed } 449193323Sed 450193323Sed if (m) 451193323Sed m_freem(m); 452193323Sed 453193323Sed return (error); 454199481Srdivacky} 455199481Srdivacky 456199481Srdivacky 457199481Srdivacky/* 458199481Srdivacky * This function is called when our device is written to. 459199481Srdivacky * We read the data from userland into mbuf chain and pass it to the remote hook. 460199481Srdivacky * 461199481Srdivacky */ 462199481Srdivackystatic int 463199481Srdivackyngdwrite(struct cdev *dev, struct uio *uio, int flag) 464199481Srdivacky{ 465199481Srdivacky priv_p priv = (priv_p )dev->si_drv1; 466199481Srdivacky struct mbuf *m; 467193323Sed int error = 0; 468199481Srdivacky 469193323Sed DBG; 470199481Srdivacky 471199481Srdivacky if (uio->uio_resid == 0) 472199481Srdivacky return (0); 473199481Srdivacky 474199481Srdivacky if (uio->uio_resid < 0 || uio->uio_resid > IP_MAXPACKET) 475199481Srdivacky return (EIO); 476199481Srdivacky 477199481Srdivacky if ((m = m_uiotombuf(uio, M_DONTWAIT, 0)) == NULL) 478199481Srdivacky return (ENOBUFS); 479199481Srdivacky 480199481Srdivacky NG_SEND_DATA_ONLY(error, priv->hook, m); 481199481Srdivacky 482199481Srdivacky return (error); 483199481Srdivacky} 484199481Srdivacky 485193323Sed/* 486199481Srdivacky * we are being polled/selected 487199481Srdivacky * check if there is data available for read 488199481Srdivacky */ 489199481Srdivackystatic int 490199481Srdivackyngdpoll(struct cdev *dev, int events, struct thread *td) 491193323Sed{ 492193323Sed priv_p priv = (priv_p )dev->si_drv1; 493198892Srdivacky int revents = 0; 494198892Srdivacky 495198892Srdivacky if (events & (POLLIN | POLLRDNORM) && 496198892Srdivacky !IFQ_IS_EMPTY(&priv->readq)) 497198892Srdivacky revents |= events & (POLLIN | POLLRDNORM); 498198892Srdivacky 499198892Srdivacky return (revents); 500198892Srdivacky} 501198892Srdivacky 502199481Srdivacky/****************************************************************************** 503199481Srdivacky * Helper subroutines 504199481Srdivacky ******************************************************************************/ 505198892Srdivacky 506198892Srdivackystatic int 507198892Srdivackyget_free_unit() 508198892Srdivacky{ 509198892Srdivacky struct ngd_private *priv = NULL; 510198892Srdivacky int n = 0; 511199481Srdivacky int unit = -1; 512199481Srdivacky 513199481Srdivacky DBG; 514199481Srdivacky 515199481Srdivacky mtx_assert(&ng_device_mtx, MA_OWNED); 516199481Srdivacky 517199481Srdivacky /* When there is no list yet, the first device unit is always 0. */ 518199481Srdivacky if SLIST_EMPTY(&ngd_nodes) 519199481Srdivacky return(0); 520199481Srdivacky 521199481Srdivacky /* Just do a brute force loop to find the first free unit that is 522199481Srdivacky * smaller than MAX_NGD. 523199481Srdivacky * Set MAX_NGD to a large value, doesn't impact performance. 524199481Srdivacky */ 525199481Srdivacky for(n = 0; n<MAX_NGD && unit == -1; n++) { 526199481Srdivacky SLIST_FOREACH(priv, &ngd_nodes, links) { 527199481Srdivacky 528199481Srdivacky if(priv->unit == n) { 529199481Srdivacky unit = -1; 530199481Srdivacky break; 531199481Srdivacky } 532199481Srdivacky unit = n; 533199481Srdivacky } 534199481Srdivacky } 535199481Srdivacky 536199481Srdivacky return (unit); 537199481Srdivacky} 538199481Srdivacky